fixed bug with high scores for wrong level when auto-playing next level
[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(void);
1089 static void FadeLevelSoundsAndMusic(void);
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite(void)
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars(void)
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig(void)
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 static void InitGameControlValues(void)
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 static void UpdatePlayfieldElementCount(void)
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 static void UpdateGameControlValues(void)
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   /* used instead of "level_nr" (for network games) */
2228   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2229   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2230
2231   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2232   for (i = 0; i < MAX_NUM_KEYS; i++)
2233     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2235   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2236
2237   if (game.centered_player_nr == -1)
2238   {
2239     for (i = 0; i < MAX_PLAYERS; i++)
2240     {
2241       /* only one player in Supaplex game engine */
2242       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2243         break;
2244
2245       for (k = 0; k < MAX_NUM_KEYS; k++)
2246       {
2247         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2248         {
2249           if (level.native_em_level->ply[i]->keys & (1 << k))
2250             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2251               get_key_element_from_nr(k);
2252         }
2253         else if (stored_player[i].key[k])
2254           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2255             get_key_element_from_nr(k);
2256       }
2257
2258       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2259         getPlayerInventorySize(i);
2260
2261       if (stored_player[i].num_white_keys > 0)
2262         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2263           EL_DC_KEY_WHITE;
2264
2265       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2266         stored_player[i].num_white_keys;
2267     }
2268   }
2269   else
2270   {
2271     int player_nr = game.centered_player_nr;
2272
2273     for (k = 0; k < MAX_NUM_KEYS; k++)
2274     {
2275       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2276       {
2277         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2278           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2279             get_key_element_from_nr(k);
2280       }
2281       else if (stored_player[player_nr].key[k])
2282         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2283           get_key_element_from_nr(k);
2284     }
2285
2286     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2287       getPlayerInventorySize(player_nr);
2288
2289     if (stored_player[player_nr].num_white_keys > 0)
2290       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2291
2292     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2293       stored_player[player_nr].num_white_keys;
2294   }
2295
2296   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2297   {
2298     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, i);
2300     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2301       get_inventory_element_from_pos(local_player, -i - 1);
2302   }
2303
2304   game_panel_controls[GAME_PANEL_SCORE].value = score;
2305   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2306
2307   game_panel_controls[GAME_PANEL_TIME].value = time;
2308
2309   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2310   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2311   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2312
2313   if (level.time == 0)
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2315   else
2316     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2317
2318   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2319   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2320
2321   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2322
2323   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2324     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2325      EL_EMPTY);
2326   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2327     local_player->shield_normal_time_left;
2328   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2329     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2330      EL_EMPTY);
2331   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2332     local_player->shield_deadly_time_left;
2333
2334   game_panel_controls[GAME_PANEL_EXIT].value =
2335     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2336
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2339   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2340     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2341      EL_EMC_MAGIC_BALL_SWITCH);
2342
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2344     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2345   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2346     game.light_time_left;
2347
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2349     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2350   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2351     game.timegate_time_left;
2352
2353   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2354     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2355
2356   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2357     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2358   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2359     game.lenses_time_left;
2360
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2362     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2363   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2364     game.magnify_time_left;
2365
2366   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2367     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2368      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2369      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2370      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2371      EL_BALLOON_SWITCH_NONE);
2372
2373   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2374     local_player->dynabomb_count;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2376     local_player->dynabomb_size;
2377   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2378     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2379
2380   game_panel_controls[GAME_PANEL_PENGUINS].value =
2381     local_player->friends_still_needed;
2382
2383   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2384     local_player->sokobanfields_still_needed;
2385   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2386     local_player->sokobanfields_still_needed;
2387
2388   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2389     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2390
2391   for (i = 0; i < NUM_BELTS; i++)
2392   {
2393     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2394       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2395        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2396     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2397       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2401     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2402   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2403     game.magic_wall_time_left;
2404
2405   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2406     local_player->gravity;
2407
2408   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2409     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2410
2411   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2412     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2413       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2414        game.panel.element[i].id : EL_UNDEFINED);
2415
2416   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2417     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2418       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2419        element_info[game.panel.element_count[i].id].element_count : 0);
2420
2421   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2422     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2423       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2424        element_info[game.panel.ce_score[i].id].collect_score : 0);
2425
2426   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2427     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2428       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2429        element_info[game.panel.ce_score_element[i].id].collect_score :
2430        EL_UNDEFINED);
2431
2432   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2434   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2435
2436   /* update game panel control frames */
2437
2438   for (i = 0; game_panel_controls[i].nr != -1; i++)
2439   {
2440     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2441
2442     if (gpc->type == TYPE_ELEMENT)
2443     {
2444       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2445       {
2446         int last_anim_random_frame = gfx.anim_random_frame;
2447         int element = gpc->value;
2448         int graphic = el2panelimg(element);
2449
2450         if (gpc->value != gpc->last_value)
2451         {
2452           gpc->gfx_frame = 0;
2453           gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455         else
2456         {
2457           gpc->gfx_frame++;
2458
2459           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2460               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2461             gpc->gfx_random = INIT_GFX_RANDOM();
2462         }
2463
2464         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2465           gfx.anim_random_frame = gpc->gfx_random;
2466
2467         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2468           gpc->gfx_frame = element_info[element].collect_score;
2469
2470         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2471                                               gpc->gfx_frame);
2472
2473         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474           gfx.anim_random_frame = last_anim_random_frame;
2475       }
2476     }
2477     else if (gpc->type == TYPE_GRAPHIC)
2478     {
2479       if (gpc->graphic != IMG_UNDEFINED)
2480       {
2481         int last_anim_random_frame = gfx.anim_random_frame;
2482         int graphic = gpc->graphic;
2483
2484         if (gpc->value != gpc->last_value)
2485         {
2486           gpc->gfx_frame = 0;
2487           gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489         else
2490         {
2491           gpc->gfx_frame++;
2492
2493           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2494               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2495             gpc->gfx_random = INIT_GFX_RANDOM();
2496         }
2497
2498         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2499           gfx.anim_random_frame = gpc->gfx_random;
2500
2501         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2502
2503         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504           gfx.anim_random_frame = last_anim_random_frame;
2505       }
2506     }
2507   }
2508 }
2509
2510 static void DisplayGameControlValues(void)
2511 {
2512   boolean redraw_panel = FALSE;
2513   int i;
2514
2515   for (i = 0; game_panel_controls[i].nr != -1; i++)
2516   {
2517     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2518
2519     if (PANEL_DEACTIVATED(gpc->pos))
2520       continue;
2521
2522     if (gpc->value == gpc->last_value &&
2523         gpc->frame == gpc->last_frame)
2524       continue;
2525
2526     redraw_panel = TRUE;
2527   }
2528
2529   if (!redraw_panel)
2530     return;
2531
2532   /* copy default game door content to main double buffer */
2533
2534   /* !!! CHECK AGAIN !!! */
2535   SetPanelBackground();
2536   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2537   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2538
2539   /* redraw game control buttons */
2540   RedrawGameButtons();
2541
2542   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2543
2544   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2545   {
2546     int nr = game_panel_order[i].nr;
2547     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2548     struct TextPosInfo *pos = gpc->pos;
2549     int type = gpc->type;
2550     int value = gpc->value;
2551     int frame = gpc->frame;
2552     int size = pos->size;
2553     int font = pos->font;
2554     boolean draw_masked = pos->draw_masked;
2555     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2556
2557     if (PANEL_DEACTIVATED(pos))
2558       continue;
2559
2560     gpc->last_value = value;
2561     gpc->last_frame = frame;
2562
2563     if (type == TYPE_INTEGER)
2564     {
2565       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2566           nr == GAME_PANEL_TIME)
2567       {
2568         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2569
2570         if (use_dynamic_size)           /* use dynamic number of digits */
2571         {
2572           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2573           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2574           int size2 = size1 + 1;
2575           int font1 = pos->font;
2576           int font2 = pos->font_alt;
2577
2578           size = (value < value_change ? size1 : size2);
2579           font = (value < value_change ? font1 : font2);
2580         }
2581       }
2582
2583       /* correct text size if "digits" is zero or less */
2584       if (size <= 0)
2585         size = strlen(int2str(value, size));
2586
2587       /* dynamically correct text alignment */
2588       pos->width = size * getFontWidth(font);
2589
2590       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591                   int2str(value, size), font, mask_mode);
2592     }
2593     else if (type == TYPE_ELEMENT)
2594     {
2595       int element, graphic;
2596       Bitmap *src_bitmap;
2597       int src_x, src_y;
2598       int width, height;
2599       int dst_x = PANEL_XPOS(pos);
2600       int dst_y = PANEL_YPOS(pos);
2601
2602       if (value != EL_UNDEFINED && value != EL_EMPTY)
2603       {
2604         element = value;
2605         graphic = el2panelimg(value);
2606
2607         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2608
2609         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2610           size = TILESIZE;
2611
2612         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2613                               &src_x, &src_y);
2614
2615         width  = graphic_info[graphic].width  * size / TILESIZE;
2616         height = graphic_info[graphic].height * size / TILESIZE;
2617
2618         if (draw_masked)
2619           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2620                            dst_x, dst_y);
2621         else
2622           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2623                      dst_x, dst_y);
2624       }
2625     }
2626     else if (type == TYPE_GRAPHIC)
2627     {
2628       int graphic        = gpc->graphic;
2629       int graphic_active = gpc->graphic_active;
2630       Bitmap *src_bitmap;
2631       int src_x, src_y;
2632       int width, height;
2633       int dst_x = PANEL_XPOS(pos);
2634       int dst_y = PANEL_YPOS(pos);
2635       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2636                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2637
2638       if (graphic != IMG_UNDEFINED && !skip)
2639       {
2640         if (pos->style == STYLE_REVERSE)
2641           value = 100 - value;
2642
2643         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2644
2645         if (pos->direction & MV_HORIZONTAL)
2646         {
2647           width  = graphic_info[graphic_active].width * value / 100;
2648           height = graphic_info[graphic_active].height;
2649
2650           if (pos->direction == MV_LEFT)
2651           {
2652             src_x += graphic_info[graphic_active].width - width;
2653             dst_x += graphic_info[graphic_active].width - width;
2654           }
2655         }
2656         else
2657         {
2658           width  = graphic_info[graphic_active].width;
2659           height = graphic_info[graphic_active].height * value / 100;
2660
2661           if (pos->direction == MV_UP)
2662           {
2663             src_y += graphic_info[graphic_active].height - height;
2664             dst_y += graphic_info[graphic_active].height - height;
2665           }
2666         }
2667
2668         if (draw_masked)
2669           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2670                            dst_x, dst_y);
2671         else
2672           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2673                      dst_x, dst_y);
2674
2675         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2676
2677         if (pos->direction & MV_HORIZONTAL)
2678         {
2679           if (pos->direction == MV_RIGHT)
2680           {
2681             src_x += width;
2682             dst_x += width;
2683           }
2684           else
2685           {
2686             dst_x = PANEL_XPOS(pos);
2687           }
2688
2689           width = graphic_info[graphic].width - width;
2690         }
2691         else
2692         {
2693           if (pos->direction == MV_DOWN)
2694           {
2695             src_y += height;
2696             dst_y += height;
2697           }
2698           else
2699           {
2700             dst_y = PANEL_YPOS(pos);
2701           }
2702
2703           height = graphic_info[graphic].height - height;
2704         }
2705
2706         if (draw_masked)
2707           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2708                            dst_x, dst_y);
2709         else
2710           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2711                      dst_x, dst_y);
2712       }
2713     }
2714     else if (type == TYPE_STRING)
2715     {
2716       boolean active = (value != 0);
2717       char *state_normal = "off";
2718       char *state_active = "on";
2719       char *state = (active ? state_active : state_normal);
2720       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2721                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2722                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2723                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2724
2725       if (nr == GAME_PANEL_GRAVITY_STATE)
2726       {
2727         int font1 = pos->font;          /* (used for normal state) */
2728         int font2 = pos->font_alt;      /* (used for active state) */
2729
2730         font = (active ? font2 : font1);
2731       }
2732
2733       if (s != NULL)
2734       {
2735         char *s_cut;
2736
2737         if (size <= 0)
2738         {
2739           /* don't truncate output if "chars" is zero or less */
2740           size = strlen(s);
2741
2742           /* dynamically correct text alignment */
2743           pos->width = size * getFontWidth(font);
2744         }
2745
2746         s_cut = getStringCopyN(s, size);
2747
2748         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2749                     s_cut, font, mask_mode);
2750
2751         free(s_cut);
2752       }
2753     }
2754
2755     redraw_mask |= REDRAW_DOOR_1;
2756   }
2757
2758   SetGameStatus(GAME_MODE_PLAYING);
2759 }
2760
2761 void UpdateAndDisplayGameControlValues(void)
2762 {
2763   if (tape.deactivate_display)
2764     return;
2765
2766   UpdateGameControlValues();
2767   DisplayGameControlValues();
2768 }
2769
2770 #if 0
2771 static void UpdateGameDoorValues(void)
2772 {
2773   UpdateGameControlValues();
2774 }
2775 #endif
2776
2777 void DrawGameDoorValues(void)
2778 {
2779   DisplayGameControlValues();
2780 }
2781
2782
2783 /*
2784   =============================================================================
2785   InitGameEngine()
2786   -----------------------------------------------------------------------------
2787   initialize game engine due to level / tape version number
2788   =============================================================================
2789 */
2790
2791 static void InitGameEngine(void)
2792 {
2793   int i, j, k, l, x, y;
2794
2795   /* set game engine from tape file when re-playing, else from level file */
2796   game.engine_version = (tape.playing ? tape.engine_version :
2797                          level.game_version);
2798
2799   /* set single or multi-player game mode (needed for re-playing tapes) */
2800   game.team_mode = setup.team_mode;
2801
2802   if (tape.playing)
2803   {
2804     int num_players = 0;
2805
2806     for (i = 0; i < MAX_PLAYERS; i++)
2807       if (tape.player_participates[i])
2808         num_players++;
2809
2810     /* multi-player tapes contain input data for more than one player */
2811     game.team_mode = (num_players > 1);
2812   }
2813
2814   /* ---------------------------------------------------------------------- */
2815   /* set flags for bugs and changes according to active game engine version */
2816   /* ---------------------------------------------------------------------- */
2817
2818   /*
2819     Summary of bugfix/change:
2820     Fixed handling for custom elements that change when pushed by the player.
2821
2822     Fixed/changed in version:
2823     3.1.0
2824
2825     Description:
2826     Before 3.1.0, custom elements that "change when pushing" changed directly
2827     after the player started pushing them (until then handled in "DigField()").
2828     Since 3.1.0, these custom elements are not changed until the "pushing"
2829     move of the element is finished (now handled in "ContinueMoving()").
2830
2831     Affected levels/tapes:
2832     The first condition is generally needed for all levels/tapes before version
2833     3.1.0, which might use the old behaviour before it was changed; known tapes
2834     that are affected are some tapes from the level set "Walpurgis Gardens" by
2835     Jamie Cullen.
2836     The second condition is an exception from the above case and is needed for
2837     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2838     above (including some development versions of 3.1.0), but before it was
2839     known that this change would break tapes like the above and was fixed in
2840     3.1.1, so that the changed behaviour was active although the engine version
2841     while recording maybe was before 3.1.0. There is at least one tape that is
2842     affected by this exception, which is the tape for the one-level set "Bug
2843     Machine" by Juergen Bonhagen.
2844   */
2845
2846   game.use_change_when_pushing_bug =
2847     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2848      !(tape.playing &&
2849        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2850        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2851
2852   /*
2853     Summary of bugfix/change:
2854     Fixed handling for blocking the field the player leaves when moving.
2855
2856     Fixed/changed in version:
2857     3.1.1
2858
2859     Description:
2860     Before 3.1.1, when "block last field when moving" was enabled, the field
2861     the player is leaving when moving was blocked for the time of the move,
2862     and was directly unblocked afterwards. This resulted in the last field
2863     being blocked for exactly one less than the number of frames of one player
2864     move. Additionally, even when blocking was disabled, the last field was
2865     blocked for exactly one frame.
2866     Since 3.1.1, due to changes in player movement handling, the last field
2867     is not blocked at all when blocking is disabled. When blocking is enabled,
2868     the last field is blocked for exactly the number of frames of one player
2869     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2870     last field is blocked for exactly one more than the number of frames of
2871     one player move.
2872
2873     Affected levels/tapes:
2874     (!!! yet to be determined -- probably many !!!)
2875   */
2876
2877   game.use_block_last_field_bug =
2878     (game.engine_version < VERSION_IDENT(3,1,1,0));
2879
2880   game_em.use_single_button =
2881     (game.engine_version > VERSION_IDENT(4,0,0,2));
2882
2883   game_em.use_snap_key_bug =
2884     (game.engine_version < VERSION_IDENT(4,0,1,0));
2885
2886   /* ---------------------------------------------------------------------- */
2887
2888   /* set maximal allowed number of custom element changes per game frame */
2889   game.max_num_changes_per_frame = 1;
2890
2891   /* default scan direction: scan playfield from top/left to bottom/right */
2892   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2893
2894   /* dynamically adjust element properties according to game engine version */
2895   InitElementPropertiesEngine(game.engine_version);
2896
2897 #if 0
2898   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2899   printf("          tape version == %06d [%s] [file: %06d]\n",
2900          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2901          tape.file_version);
2902   printf("       => game.engine_version == %06d\n", game.engine_version);
2903 #endif
2904
2905   /* ---------- initialize player's initial move delay --------------------- */
2906
2907   /* dynamically adjust player properties according to level information */
2908   for (i = 0; i < MAX_PLAYERS; i++)
2909     game.initial_move_delay_value[i] =
2910       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2911
2912   /* dynamically adjust player properties according to game engine version */
2913   for (i = 0; i < MAX_PLAYERS; i++)
2914     game.initial_move_delay[i] =
2915       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2916        game.initial_move_delay_value[i] : 0);
2917
2918   /* ---------- initialize player's initial push delay --------------------- */
2919
2920   /* dynamically adjust player properties according to game engine version */
2921   game.initial_push_delay_value =
2922     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2923
2924   /* ---------- initialize changing elements ------------------------------- */
2925
2926   /* initialize changing elements information */
2927   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2928   {
2929     struct ElementInfo *ei = &element_info[i];
2930
2931     /* this pointer might have been changed in the level editor */
2932     ei->change = &ei->change_page[0];
2933
2934     if (!IS_CUSTOM_ELEMENT(i))
2935     {
2936       ei->change->target_element = EL_EMPTY_SPACE;
2937       ei->change->delay_fixed = 0;
2938       ei->change->delay_random = 0;
2939       ei->change->delay_frames = 1;
2940     }
2941
2942     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2943     {
2944       ei->has_change_event[j] = FALSE;
2945
2946       ei->event_page_nr[j] = 0;
2947       ei->event_page[j] = &ei->change_page[0];
2948     }
2949   }
2950
2951   /* add changing elements from pre-defined list */
2952   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2953   {
2954     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2955     struct ElementInfo *ei = &element_info[ch_delay->element];
2956
2957     ei->change->target_element       = ch_delay->target_element;
2958     ei->change->delay_fixed          = ch_delay->change_delay;
2959
2960     ei->change->pre_change_function  = ch_delay->pre_change_function;
2961     ei->change->change_function      = ch_delay->change_function;
2962     ei->change->post_change_function = ch_delay->post_change_function;
2963
2964     ei->change->can_change = TRUE;
2965     ei->change->can_change_or_has_action = TRUE;
2966
2967     ei->has_change_event[CE_DELAY] = TRUE;
2968
2969     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2970     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2971   }
2972
2973   /* ---------- initialize internal run-time variables --------------------- */
2974
2975   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2976   {
2977     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2978
2979     for (j = 0; j < ei->num_change_pages; j++)
2980     {
2981       ei->change_page[j].can_change_or_has_action =
2982         (ei->change_page[j].can_change |
2983          ei->change_page[j].has_action);
2984     }
2985   }
2986
2987   /* add change events from custom element configuration */
2988   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2989   {
2990     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2991
2992     for (j = 0; j < ei->num_change_pages; j++)
2993     {
2994       if (!ei->change_page[j].can_change_or_has_action)
2995         continue;
2996
2997       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2998       {
2999         /* only add event page for the first page found with this event */
3000         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3001         {
3002           ei->has_change_event[k] = TRUE;
3003
3004           ei->event_page_nr[k] = j;
3005           ei->event_page[k] = &ei->change_page[j];
3006         }
3007       }
3008     }
3009   }
3010
3011   /* ---------- initialize reference elements in change conditions --------- */
3012
3013   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3014   {
3015     int element = EL_CUSTOM_START + i;
3016     struct ElementInfo *ei = &element_info[element];
3017
3018     for (j = 0; j < ei->num_change_pages; j++)
3019     {
3020       int trigger_element = ei->change_page[j].initial_trigger_element;
3021
3022       if (trigger_element >= EL_PREV_CE_8 &&
3023           trigger_element <= EL_NEXT_CE_8)
3024         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3025
3026       ei->change_page[j].trigger_element = trigger_element;
3027     }
3028   }
3029
3030   /* ---------- initialize run-time trigger player and element ------------- */
3031
3032   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3033   {
3034     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3035
3036     for (j = 0; j < ei->num_change_pages; j++)
3037     {
3038       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3039       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3040       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3041       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3042       ei->change_page[j].actual_trigger_ce_value = 0;
3043       ei->change_page[j].actual_trigger_ce_score = 0;
3044     }
3045   }
3046
3047   /* ---------- initialize trigger events ---------------------------------- */
3048
3049   /* initialize trigger events information */
3050   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3051     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3052       trigger_events[i][j] = FALSE;
3053
3054   /* add trigger events from element change event properties */
3055   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3056   {
3057     struct ElementInfo *ei = &element_info[i];
3058
3059     for (j = 0; j < ei->num_change_pages; j++)
3060     {
3061       if (!ei->change_page[j].can_change_or_has_action)
3062         continue;
3063
3064       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3065       {
3066         int trigger_element = ei->change_page[j].trigger_element;
3067
3068         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3069         {
3070           if (ei->change_page[j].has_event[k])
3071           {
3072             if (IS_GROUP_ELEMENT(trigger_element))
3073             {
3074               struct ElementGroupInfo *group =
3075                 element_info[trigger_element].group;
3076
3077               for (l = 0; l < group->num_elements_resolved; l++)
3078                 trigger_events[group->element_resolved[l]][k] = TRUE;
3079             }
3080             else if (trigger_element == EL_ANY_ELEMENT)
3081               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3082                 trigger_events[l][k] = TRUE;
3083             else
3084               trigger_events[trigger_element][k] = TRUE;
3085           }
3086         }
3087       }
3088     }
3089   }
3090
3091   /* ---------- initialize push delay -------------------------------------- */
3092
3093   /* initialize push delay values to default */
3094   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3095   {
3096     if (!IS_CUSTOM_ELEMENT(i))
3097     {
3098       /* set default push delay values (corrected since version 3.0.7-1) */
3099       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3100       {
3101         element_info[i].push_delay_fixed = 2;
3102         element_info[i].push_delay_random = 8;
3103       }
3104       else
3105       {
3106         element_info[i].push_delay_fixed = 8;
3107         element_info[i].push_delay_random = 8;
3108       }
3109     }
3110   }
3111
3112   /* set push delay value for certain elements from pre-defined list */
3113   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3114   {
3115     int e = push_delay_list[i].element;
3116
3117     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3118     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3119   }
3120
3121   /* set push delay value for Supaplex elements for newer engine versions */
3122   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3123   {
3124     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3125     {
3126       if (IS_SP_ELEMENT(i))
3127       {
3128         /* set SP push delay to just enough to push under a falling zonk */
3129         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3130
3131         element_info[i].push_delay_fixed  = delay;
3132         element_info[i].push_delay_random = 0;
3133       }
3134     }
3135   }
3136
3137   /* ---------- initialize move stepsize ----------------------------------- */
3138
3139   /* initialize move stepsize values to default */
3140   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3141     if (!IS_CUSTOM_ELEMENT(i))
3142       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3143
3144   /* set move stepsize value for certain elements from pre-defined list */
3145   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3146   {
3147     int e = move_stepsize_list[i].element;
3148
3149     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3150   }
3151
3152   /* ---------- initialize collect score ----------------------------------- */
3153
3154   /* initialize collect score values for custom elements from initial value */
3155   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3156     if (IS_CUSTOM_ELEMENT(i))
3157       element_info[i].collect_score = element_info[i].collect_score_initial;
3158
3159   /* ---------- initialize collect count ----------------------------------- */
3160
3161   /* initialize collect count values for non-custom elements */
3162   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3163     if (!IS_CUSTOM_ELEMENT(i))
3164       element_info[i].collect_count_initial = 0;
3165
3166   /* add collect count values for all elements from pre-defined list */
3167   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3168     element_info[collect_count_list[i].element].collect_count_initial =
3169       collect_count_list[i].count;
3170
3171   /* ---------- initialize access direction -------------------------------- */
3172
3173   /* initialize access direction values to default (access from every side) */
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175     if (!IS_CUSTOM_ELEMENT(i))
3176       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3177
3178   /* set access direction value for certain elements from pre-defined list */
3179   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3180     element_info[access_direction_list[i].element].access_direction =
3181       access_direction_list[i].direction;
3182
3183   /* ---------- initialize explosion content ------------------------------- */
3184   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3185   {
3186     if (IS_CUSTOM_ELEMENT(i))
3187       continue;
3188
3189     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3190     {
3191       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3192
3193       element_info[i].content.e[x][y] =
3194         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3195          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3196          i == EL_PLAYER_3 ? EL_EMERALD :
3197          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3198          i == EL_MOLE ? EL_EMERALD_RED :
3199          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3200          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3201          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3202          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3203          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3204          i == EL_WALL_EMERALD ? EL_EMERALD :
3205          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3206          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3207          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3208          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3209          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3210          i == EL_WALL_PEARL ? EL_PEARL :
3211          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3212          EL_EMPTY);
3213     }
3214   }
3215
3216   /* ---------- initialize recursion detection ------------------------------ */
3217   recursion_loop_depth = 0;
3218   recursion_loop_detected = FALSE;
3219   recursion_loop_element = EL_UNDEFINED;
3220
3221   /* ---------- initialize graphics engine ---------------------------------- */
3222   game.scroll_delay_value =
3223     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3224      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3225   game.scroll_delay_value =
3226     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3227
3228   /* ---------- initialize game engine snapshots ---------------------------- */
3229   for (i = 0; i < MAX_PLAYERS; i++)
3230     game.snapshot.last_action[i] = 0;
3231   game.snapshot.changed_action = FALSE;
3232   game.snapshot.collected_item = FALSE;
3233   game.snapshot.mode =
3234     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3235      SNAPSHOT_MODE_EVERY_STEP :
3236      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3237      SNAPSHOT_MODE_EVERY_MOVE :
3238      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3239      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3240   game.snapshot.save_snapshot = FALSE;
3241
3242   /* ---------- initialize level time for Supaplex engine ------------------- */
3243   /* Supaplex levels with time limit currently unsupported -- should be added */
3244   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3245     level.time = 0;
3246 }
3247
3248 static int get_num_special_action(int element, int action_first,
3249                                   int action_last)
3250 {
3251   int num_special_action = 0;
3252   int i, j;
3253
3254   for (i = action_first; i <= action_last; i++)
3255   {
3256     boolean found = FALSE;
3257
3258     for (j = 0; j < NUM_DIRECTIONS; j++)
3259       if (el_act_dir2img(element, i, j) !=
3260           el_act_dir2img(element, ACTION_DEFAULT, j))
3261         found = TRUE;
3262
3263     if (found)
3264       num_special_action++;
3265     else
3266       break;
3267   }
3268
3269   return num_special_action;
3270 }
3271
3272
3273 /*
3274   =============================================================================
3275   InitGame()
3276   -----------------------------------------------------------------------------
3277   initialize and start new game
3278   =============================================================================
3279 */
3280
3281 #if DEBUG_INIT_PLAYER
3282 static void DebugPrintPlayerStatus(char *message)
3283 {
3284   int i;
3285
3286   if (!options.debug)
3287     return;
3288
3289   printf("%s:\n", message);
3290
3291   for (i = 0; i < MAX_PLAYERS; i++)
3292   {
3293     struct PlayerInfo *player = &stored_player[i];
3294
3295     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3296            i + 1,
3297            player->present,
3298            player->connected,
3299            player->connected_locally,
3300            player->connected_network,
3301            player->active);
3302
3303     if (local_player == player)
3304       printf(" (local player)");
3305
3306     printf("\n");
3307   }
3308 }
3309 #endif
3310
3311 void InitGame(void)
3312 {
3313   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3314   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3315   int fade_mask = REDRAW_FIELD;
3316
3317   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3318   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3319   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3320   int initial_move_dir = MV_DOWN;
3321   int i, j, x, y;
3322
3323   // required here to update video display before fading (FIX THIS)
3324   DrawMaskedBorder(REDRAW_DOOR_2);
3325
3326   if (!game.restart_level)
3327     CloseDoor(DOOR_CLOSE_1);
3328
3329   SetGameStatus(GAME_MODE_PLAYING);
3330
3331   if (level_editor_test_game)
3332     FadeSkipNextFadeIn();
3333   else
3334     FadeSetEnterScreen();
3335
3336   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3337     fade_mask = REDRAW_ALL;
3338
3339   FadeLevelSoundsAndMusic();
3340
3341   ExpireSoundLoops(TRUE);
3342
3343   FadeOut(fade_mask);
3344
3345   /* needed if different viewport properties defined for playing */
3346   ChangeViewportPropertiesIfNeeded();
3347
3348   ClearField();
3349
3350   DrawCompleteVideoDisplay();
3351
3352   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3353
3354   InitGameEngine();
3355   InitGameControlValues();
3356
3357   /* don't play tapes over network */
3358   network_playing = (network.enabled && !tape.playing);
3359
3360   for (i = 0; i < MAX_PLAYERS; i++)
3361   {
3362     struct PlayerInfo *player = &stored_player[i];
3363
3364     player->index_nr = i;
3365     player->index_bit = (1 << i);
3366     player->element_nr = EL_PLAYER_1 + i;
3367
3368     player->present = FALSE;
3369     player->active = FALSE;
3370     player->mapped = FALSE;
3371
3372     player->killed = FALSE;
3373     player->reanimated = FALSE;
3374
3375     player->action = 0;
3376     player->effective_action = 0;
3377     player->programmed_action = 0;
3378
3379     player->mouse_action.lx = 0;
3380     player->mouse_action.ly = 0;
3381     player->mouse_action.button = 0;
3382     player->mouse_action.button_hint = 0;
3383
3384     player->effective_mouse_action.lx = 0;
3385     player->effective_mouse_action.ly = 0;
3386     player->effective_mouse_action.button = 0;
3387     player->effective_mouse_action.button_hint = 0;
3388
3389     player->score = 0;
3390     player->score_final = 0;
3391
3392     player->health = MAX_HEALTH;
3393     player->health_final = MAX_HEALTH;
3394
3395     player->gems_still_needed = level.gems_needed;
3396     player->sokobanfields_still_needed = 0;
3397     player->lights_still_needed = 0;
3398     player->players_still_needed = 0;
3399     player->friends_still_needed = 0;
3400
3401     for (j = 0; j < MAX_NUM_KEYS; j++)
3402       player->key[j] = FALSE;
3403
3404     player->num_white_keys = 0;
3405
3406     player->dynabomb_count = 0;
3407     player->dynabomb_size = 1;
3408     player->dynabombs_left = 0;
3409     player->dynabomb_xl = FALSE;
3410
3411     player->MovDir = initial_move_dir;
3412     player->MovPos = 0;
3413     player->GfxPos = 0;
3414     player->GfxDir = initial_move_dir;
3415     player->GfxAction = ACTION_DEFAULT;
3416     player->Frame = 0;
3417     player->StepFrame = 0;
3418
3419     player->initial_element = player->element_nr;
3420     player->artwork_element =
3421       (level.use_artwork_element[i] ? level.artwork_element[i] :
3422        player->element_nr);
3423     player->use_murphy = FALSE;
3424
3425     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3426     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3427
3428     player->gravity = level.initial_player_gravity[i];
3429
3430     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3431
3432     player->actual_frame_counter = 0;
3433
3434     player->step_counter = 0;
3435
3436     player->last_move_dir = initial_move_dir;
3437
3438     player->is_active = FALSE;
3439
3440     player->is_waiting = FALSE;
3441     player->is_moving = FALSE;
3442     player->is_auto_moving = FALSE;
3443     player->is_digging = FALSE;
3444     player->is_snapping = FALSE;
3445     player->is_collecting = FALSE;
3446     player->is_pushing = FALSE;
3447     player->is_switching = FALSE;
3448     player->is_dropping = FALSE;
3449     player->is_dropping_pressed = FALSE;
3450
3451     player->is_bored = FALSE;
3452     player->is_sleeping = FALSE;
3453
3454     player->was_waiting = TRUE;
3455     player->was_moving = FALSE;
3456     player->was_snapping = FALSE;
3457     player->was_dropping = FALSE;
3458
3459     player->force_dropping = FALSE;
3460
3461     player->frame_counter_bored = -1;
3462     player->frame_counter_sleeping = -1;
3463
3464     player->anim_delay_counter = 0;
3465     player->post_delay_counter = 0;
3466
3467     player->dir_waiting = initial_move_dir;
3468     player->action_waiting = ACTION_DEFAULT;
3469     player->last_action_waiting = ACTION_DEFAULT;
3470     player->special_action_bored = ACTION_DEFAULT;
3471     player->special_action_sleeping = ACTION_DEFAULT;
3472
3473     player->switch_x = -1;
3474     player->switch_y = -1;
3475
3476     player->drop_x = -1;
3477     player->drop_y = -1;
3478
3479     player->show_envelope = 0;
3480
3481     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3482
3483     player->push_delay       = -1;      /* initialized when pushing starts */
3484     player->push_delay_value = game.initial_push_delay_value;
3485
3486     player->drop_delay = 0;
3487     player->drop_pressed_delay = 0;
3488
3489     player->last_jx = -1;
3490     player->last_jy = -1;
3491     player->jx = -1;
3492     player->jy = -1;
3493
3494     player->shield_normal_time_left = 0;
3495     player->shield_deadly_time_left = 0;
3496
3497     player->inventory_infinite_element = EL_UNDEFINED;
3498     player->inventory_size = 0;
3499
3500     if (level.use_initial_inventory[i])
3501     {
3502       for (j = 0; j < level.initial_inventory_size[i]; j++)
3503       {
3504         int element = level.initial_inventory_content[i][j];
3505         int collect_count = element_info[element].collect_count_initial;
3506         int k;
3507
3508         if (!IS_CUSTOM_ELEMENT(element))
3509           collect_count = 1;
3510
3511         if (collect_count == 0)
3512           player->inventory_infinite_element = element;
3513         else
3514           for (k = 0; k < collect_count; k++)
3515             if (player->inventory_size < MAX_INVENTORY_SIZE)
3516               player->inventory_element[player->inventory_size++] = element;
3517       }
3518     }
3519
3520     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3521     SnapField(player, 0, 0);
3522
3523     player->LevelSolved = FALSE;
3524     player->GameOver = FALSE;
3525
3526     player->LevelSolved_GameWon = FALSE;
3527     player->LevelSolved_GameEnd = FALSE;
3528     player->LevelSolved_PanelOff = FALSE;
3529     player->LevelSolved_SaveTape = FALSE;
3530     player->LevelSolved_SaveScore = FALSE;
3531
3532     player->LevelSolved_CountingTime = 0;
3533     player->LevelSolved_CountingScore = 0;
3534     player->LevelSolved_CountingHealth = 0;
3535
3536     map_player_action[i] = i;
3537   }
3538
3539   network_player_action_received = FALSE;
3540
3541   /* initial null action */
3542   if (network_playing)
3543     SendToServer_MovePlayer(MV_NONE);
3544
3545   ZX = ZY = -1;
3546   ExitX = ExitY = -1;
3547
3548   FrameCounter = 0;
3549   TimeFrames = 0;
3550   TimePlayed = 0;
3551   TimeLeft = level.time;
3552   TapeTime = 0;
3553
3554   ScreenMovDir = MV_NONE;
3555   ScreenMovPos = 0;
3556   ScreenGfxPos = 0;
3557
3558   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3559
3560   AllPlayersGone = FALSE;
3561
3562   game.no_time_limit = (level.time == 0);
3563
3564   game.yamyam_content_nr = 0;
3565   game.robot_wheel_active = FALSE;
3566   game.magic_wall_active = FALSE;
3567   game.magic_wall_time_left = 0;
3568   game.light_time_left = 0;
3569   game.timegate_time_left = 0;
3570   game.switchgate_pos = 0;
3571   game.wind_direction = level.wind_direction_initial;
3572
3573   game.lenses_time_left = 0;
3574   game.magnify_time_left = 0;
3575
3576   game.ball_state = level.ball_state_initial;
3577   game.ball_content_nr = 0;
3578
3579   game.explosions_delayed = TRUE;
3580
3581   game.envelope_active = FALSE;
3582
3583   for (i = 0; i < NUM_BELTS; i++)
3584   {
3585     game.belt_dir[i] = MV_NONE;
3586     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3587   }
3588
3589   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3590     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3591
3592 #if DEBUG_INIT_PLAYER
3593   DebugPrintPlayerStatus("Player status at level initialization");
3594 #endif
3595
3596   SCAN_PLAYFIELD(x, y)
3597   {
3598     Feld[x][y] = Last[x][y] = level.field[x][y];
3599     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3600     ChangeDelay[x][y] = 0;
3601     ChangePage[x][y] = -1;
3602     CustomValue[x][y] = 0;              /* initialized in InitField() */
3603     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3604     AmoebaNr[x][y] = 0;
3605     WasJustMoving[x][y] = 0;
3606     WasJustFalling[x][y] = 0;
3607     CheckCollision[x][y] = 0;
3608     CheckImpact[x][y] = 0;
3609     Stop[x][y] = FALSE;
3610     Pushed[x][y] = FALSE;
3611
3612     ChangeCount[x][y] = 0;
3613     ChangeEvent[x][y] = -1;
3614
3615     ExplodePhase[x][y] = 0;
3616     ExplodeDelay[x][y] = 0;
3617     ExplodeField[x][y] = EX_TYPE_NONE;
3618
3619     RunnerVisit[x][y] = 0;
3620     PlayerVisit[x][y] = 0;
3621
3622     GfxFrame[x][y] = 0;
3623     GfxRandom[x][y] = INIT_GFX_RANDOM();
3624     GfxElement[x][y] = EL_UNDEFINED;
3625     GfxAction[x][y] = ACTION_DEFAULT;
3626     GfxDir[x][y] = MV_NONE;
3627     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3628   }
3629
3630   SCAN_PLAYFIELD(x, y)
3631   {
3632     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3633       emulate_bd = FALSE;
3634     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3635       emulate_sb = FALSE;
3636     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3637       emulate_sp = FALSE;
3638
3639     InitField(x, y, TRUE);
3640
3641     ResetGfxAnimation(x, y);
3642   }
3643
3644   InitBeltMovement();
3645
3646   for (i = 0; i < MAX_PLAYERS; i++)
3647   {
3648     struct PlayerInfo *player = &stored_player[i];
3649
3650     /* set number of special actions for bored and sleeping animation */
3651     player->num_special_action_bored =
3652       get_num_special_action(player->artwork_element,
3653                              ACTION_BORING_1, ACTION_BORING_LAST);
3654     player->num_special_action_sleeping =
3655       get_num_special_action(player->artwork_element,
3656                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3657   }
3658
3659   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3660                     emulate_sb ? EMU_SOKOBAN :
3661                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3662
3663   /* initialize type of slippery elements */
3664   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3665   {
3666     if (!IS_CUSTOM_ELEMENT(i))
3667     {
3668       /* default: elements slip down either to the left or right randomly */
3669       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3670
3671       /* SP style elements prefer to slip down on the left side */
3672       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3673         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3674
3675       /* BD style elements prefer to slip down on the left side */
3676       if (game.emulation == EMU_BOULDERDASH)
3677         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3678     }
3679   }
3680
3681   /* initialize explosion and ignition delay */
3682   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3683   {
3684     if (!IS_CUSTOM_ELEMENT(i))
3685     {
3686       int num_phase = 8;
3687       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3688                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3689                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3690       int last_phase = (num_phase + 1) * delay;
3691       int half_phase = (num_phase / 2) * delay;
3692
3693       element_info[i].explosion_delay = last_phase - 1;
3694       element_info[i].ignition_delay = half_phase;
3695
3696       if (i == EL_BLACK_ORB)
3697         element_info[i].ignition_delay = 1;
3698     }
3699   }
3700
3701   /* correct non-moving belts to start moving left */
3702   for (i = 0; i < NUM_BELTS; i++)
3703     if (game.belt_dir[i] == MV_NONE)
3704       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3705
3706 #if USE_NEW_PLAYER_ASSIGNMENTS
3707   for (i = 0; i < MAX_PLAYERS; i++)
3708   {
3709     stored_player[i].connected = FALSE;
3710
3711     /* in network game mode, the local player might not be the first player */
3712     if (stored_player[i].connected_locally)
3713       local_player = &stored_player[i];
3714   }
3715
3716   if (!network.enabled)
3717     local_player->connected = TRUE;
3718
3719   if (tape.playing)
3720   {
3721     for (i = 0; i < MAX_PLAYERS; i++)
3722       stored_player[i].connected = tape.player_participates[i];
3723   }
3724   else if (network.enabled)
3725   {
3726     /* add team mode players connected over the network (needed for correct
3727        assignment of player figures from level to locally playing players) */
3728
3729     for (i = 0; i < MAX_PLAYERS; i++)
3730       if (stored_player[i].connected_network)
3731         stored_player[i].connected = TRUE;
3732   }
3733   else if (game.team_mode)
3734   {
3735     /* try to guess locally connected team mode players (needed for correct
3736        assignment of player figures from level to locally playing players) */
3737
3738     for (i = 0; i < MAX_PLAYERS; i++)
3739       if (setup.input[i].use_joystick ||
3740           setup.input[i].key.left != KSYM_UNDEFINED)
3741         stored_player[i].connected = TRUE;
3742   }
3743
3744 #if DEBUG_INIT_PLAYER
3745   DebugPrintPlayerStatus("Player status after level initialization");
3746 #endif
3747
3748 #if DEBUG_INIT_PLAYER
3749   if (options.debug)
3750     printf("Reassigning players ...\n");
3751 #endif
3752
3753   /* check if any connected player was not found in playfield */
3754   for (i = 0; i < MAX_PLAYERS; i++)
3755   {
3756     struct PlayerInfo *player = &stored_player[i];
3757
3758     if (player->connected && !player->present)
3759     {
3760       struct PlayerInfo *field_player = NULL;
3761
3762 #if DEBUG_INIT_PLAYER
3763       if (options.debug)
3764         printf("- looking for field player for player %d ...\n", i + 1);
3765 #endif
3766
3767       /* assign first free player found that is present in the playfield */
3768
3769       /* first try: look for unmapped playfield player that is not connected */
3770       for (j = 0; j < MAX_PLAYERS; j++)
3771         if (field_player == NULL &&
3772             stored_player[j].present &&
3773             !stored_player[j].mapped &&
3774             !stored_player[j].connected)
3775           field_player = &stored_player[j];
3776
3777       /* second try: look for *any* unmapped playfield player */
3778       for (j = 0; j < MAX_PLAYERS; j++)
3779         if (field_player == NULL &&
3780             stored_player[j].present &&
3781             !stored_player[j].mapped)
3782           field_player = &stored_player[j];
3783
3784       if (field_player != NULL)
3785       {
3786         int jx = field_player->jx, jy = field_player->jy;
3787
3788 #if DEBUG_INIT_PLAYER
3789         if (options.debug)
3790           printf("- found player %d\n", field_player->index_nr + 1);
3791 #endif
3792
3793         player->present = FALSE;
3794         player->active = FALSE;
3795
3796         field_player->present = TRUE;
3797         field_player->active = TRUE;
3798
3799         /*
3800         player->initial_element = field_player->initial_element;
3801         player->artwork_element = field_player->artwork_element;
3802
3803         player->block_last_field       = field_player->block_last_field;
3804         player->block_delay_adjustment = field_player->block_delay_adjustment;
3805         */
3806
3807         StorePlayer[jx][jy] = field_player->element_nr;
3808
3809         field_player->jx = field_player->last_jx = jx;
3810         field_player->jy = field_player->last_jy = jy;
3811
3812         if (local_player == player)
3813           local_player = field_player;
3814
3815         map_player_action[field_player->index_nr] = i;
3816
3817         field_player->mapped = TRUE;
3818
3819 #if DEBUG_INIT_PLAYER
3820         if (options.debug)
3821           printf("- map_player_action[%d] == %d\n",
3822                  field_player->index_nr + 1, i + 1);
3823 #endif
3824       }
3825     }
3826
3827     if (player->connected && player->present)
3828       player->mapped = TRUE;
3829   }
3830
3831 #if DEBUG_INIT_PLAYER
3832   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3833 #endif
3834
3835 #else
3836
3837   /* check if any connected player was not found in playfield */
3838   for (i = 0; i < MAX_PLAYERS; i++)
3839   {
3840     struct PlayerInfo *player = &stored_player[i];
3841
3842     if (player->connected && !player->present)
3843     {
3844       for (j = 0; j < MAX_PLAYERS; j++)
3845       {
3846         struct PlayerInfo *field_player = &stored_player[j];
3847         int jx = field_player->jx, jy = field_player->jy;
3848
3849         /* assign first free player found that is present in the playfield */
3850         if (field_player->present && !field_player->connected)
3851         {
3852           player->present = TRUE;
3853           player->active = TRUE;
3854
3855           field_player->present = FALSE;
3856           field_player->active = FALSE;
3857
3858           player->initial_element = field_player->initial_element;
3859           player->artwork_element = field_player->artwork_element;
3860
3861           player->block_last_field       = field_player->block_last_field;
3862           player->block_delay_adjustment = field_player->block_delay_adjustment;
3863
3864           StorePlayer[jx][jy] = player->element_nr;
3865
3866           player->jx = player->last_jx = jx;
3867           player->jy = player->last_jy = jy;
3868
3869           break;
3870         }
3871       }
3872     }
3873   }
3874 #endif
3875
3876 #if 0
3877   printf("::: local_player->present == %d\n", local_player->present);
3878 #endif
3879
3880   /* set focus to local player for network games, else to all players */
3881   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3882   game.centered_player_nr_next = game.centered_player_nr;
3883   game.set_centered_player = FALSE;
3884
3885   if (network_playing && tape.recording)
3886   {
3887     /* store client dependent player focus when recording network games */
3888     tape.centered_player_nr_next = game.centered_player_nr_next;
3889     tape.set_centered_player = TRUE;
3890   }
3891
3892   if (tape.playing)
3893   {
3894     /* when playing a tape, eliminate all players who do not participate */
3895
3896 #if USE_NEW_PLAYER_ASSIGNMENTS
3897
3898     if (!game.team_mode)
3899     {
3900       for (i = 0; i < MAX_PLAYERS; i++)
3901       {
3902         if (stored_player[i].active &&
3903             !tape.player_participates[map_player_action[i]])
3904         {
3905           struct PlayerInfo *player = &stored_player[i];
3906           int jx = player->jx, jy = player->jy;
3907
3908 #if DEBUG_INIT_PLAYER
3909           if (options.debug)
3910             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3911 #endif
3912
3913           player->active = FALSE;
3914           StorePlayer[jx][jy] = 0;
3915           Feld[jx][jy] = EL_EMPTY;
3916         }
3917       }
3918     }
3919
3920 #else
3921
3922     for (i = 0; i < MAX_PLAYERS; i++)
3923     {
3924       if (stored_player[i].active &&
3925           !tape.player_participates[i])
3926       {
3927         struct PlayerInfo *player = &stored_player[i];
3928         int jx = player->jx, jy = player->jy;
3929
3930         player->active = FALSE;
3931         StorePlayer[jx][jy] = 0;
3932         Feld[jx][jy] = EL_EMPTY;
3933       }
3934     }
3935 #endif
3936   }
3937   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3938   {
3939     /* when in single player mode, eliminate all but the local player */
3940
3941     for (i = 0; i < MAX_PLAYERS; i++)
3942     {
3943       struct PlayerInfo *player = &stored_player[i];
3944
3945       if (player->active && player != local_player)
3946       {
3947         int jx = player->jx, jy = player->jy;
3948
3949         player->active = FALSE;
3950         player->present = FALSE;
3951
3952         StorePlayer[jx][jy] = 0;
3953         Feld[jx][jy] = EL_EMPTY;
3954       }
3955     }
3956   }
3957
3958   for (i = 0; i < MAX_PLAYERS; i++)
3959     if (stored_player[i].active)
3960       local_player->players_still_needed++;
3961
3962   if (level.solved_by_one_player)
3963     local_player->players_still_needed = 1;
3964
3965   /* when recording the game, store which players take part in the game */
3966   if (tape.recording)
3967   {
3968 #if USE_NEW_PLAYER_ASSIGNMENTS
3969     for (i = 0; i < MAX_PLAYERS; i++)
3970       if (stored_player[i].connected)
3971         tape.player_participates[i] = TRUE;
3972 #else
3973     for (i = 0; i < MAX_PLAYERS; i++)
3974       if (stored_player[i].active)
3975         tape.player_participates[i] = TRUE;
3976 #endif
3977   }
3978
3979 #if DEBUG_INIT_PLAYER
3980   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3981 #endif
3982
3983   if (BorderElement == EL_EMPTY)
3984   {
3985     SBX_Left = 0;
3986     SBX_Right = lev_fieldx - SCR_FIELDX;
3987     SBY_Upper = 0;
3988     SBY_Lower = lev_fieldy - SCR_FIELDY;
3989   }
3990   else
3991   {
3992     SBX_Left = -1;
3993     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3994     SBY_Upper = -1;
3995     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3996   }
3997
3998   if (full_lev_fieldx <= SCR_FIELDX)
3999     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4000   if (full_lev_fieldy <= SCR_FIELDY)
4001     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4002
4003   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4004     SBX_Left--;
4005   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4006     SBY_Upper--;
4007
4008   /* if local player not found, look for custom element that might create
4009      the player (make some assumptions about the right custom element) */
4010   if (!local_player->present)
4011   {
4012     int start_x = 0, start_y = 0;
4013     int found_rating = 0;
4014     int found_element = EL_UNDEFINED;
4015     int player_nr = local_player->index_nr;
4016
4017     SCAN_PLAYFIELD(x, y)
4018     {
4019       int element = Feld[x][y];
4020       int content;
4021       int xx, yy;
4022       boolean is_player;
4023
4024       if (level.use_start_element[player_nr] &&
4025           level.start_element[player_nr] == element &&
4026           found_rating < 4)
4027       {
4028         start_x = x;
4029         start_y = y;
4030
4031         found_rating = 4;
4032         found_element = element;
4033       }
4034
4035       if (!IS_CUSTOM_ELEMENT(element))
4036         continue;
4037
4038       if (CAN_CHANGE(element))
4039       {
4040         for (i = 0; i < element_info[element].num_change_pages; i++)
4041         {
4042           /* check for player created from custom element as single target */
4043           content = element_info[element].change_page[i].target_element;
4044           is_player = ELEM_IS_PLAYER(content);
4045
4046           if (is_player && (found_rating < 3 ||
4047                             (found_rating == 3 && element < found_element)))
4048           {
4049             start_x = x;
4050             start_y = y;
4051
4052             found_rating = 3;
4053             found_element = element;
4054           }
4055         }
4056       }
4057
4058       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4059       {
4060         /* check for player created from custom element as explosion content */
4061         content = element_info[element].content.e[xx][yy];
4062         is_player = ELEM_IS_PLAYER(content);
4063
4064         if (is_player && (found_rating < 2 ||
4065                           (found_rating == 2 && element < found_element)))
4066         {
4067           start_x = x + xx - 1;
4068           start_y = y + yy - 1;
4069
4070           found_rating = 2;
4071           found_element = element;
4072         }
4073
4074         if (!CAN_CHANGE(element))
4075           continue;
4076
4077         for (i = 0; i < element_info[element].num_change_pages; i++)
4078         {
4079           /* check for player created from custom element as extended target */
4080           content =
4081             element_info[element].change_page[i].target_content.e[xx][yy];
4082
4083           is_player = ELEM_IS_PLAYER(content);
4084
4085           if (is_player && (found_rating < 1 ||
4086                             (found_rating == 1 && element < found_element)))
4087           {
4088             start_x = x + xx - 1;
4089             start_y = y + yy - 1;
4090
4091             found_rating = 1;
4092             found_element = element;
4093           }
4094         }
4095       }
4096     }
4097
4098     scroll_x = SCROLL_POSITION_X(start_x);
4099     scroll_y = SCROLL_POSITION_Y(start_y);
4100   }
4101   else
4102   {
4103     scroll_x = SCROLL_POSITION_X(local_player->jx);
4104     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4105   }
4106
4107   /* !!! FIX THIS (START) !!! */
4108   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4109   {
4110     InitGameEngine_EM();
4111   }
4112   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4113   {
4114     InitGameEngine_SP();
4115   }
4116   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4117   {
4118     InitGameEngine_MM();
4119   }
4120   else
4121   {
4122     DrawLevel(REDRAW_FIELD);
4123     DrawAllPlayers();
4124
4125     /* after drawing the level, correct some elements */
4126     if (game.timegate_time_left == 0)
4127       CloseAllOpenTimegates();
4128   }
4129
4130   /* blit playfield from scroll buffer to normal back buffer for fading in */
4131   BlitScreenToBitmap(backbuffer);
4132   /* !!! FIX THIS (END) !!! */
4133
4134   DrawMaskedBorder(fade_mask);
4135
4136   FadeIn(fade_mask);
4137
4138 #if 1
4139   // full screen redraw is required at this point in the following cases:
4140   // - special editor door undrawn when game was started from level editor
4141   // - drawing area (playfield) was changed and has to be removed completely
4142   redraw_mask = REDRAW_ALL;
4143   BackToFront();
4144 #endif
4145
4146   if (!game.restart_level)
4147   {
4148     /* copy default game door content to main double buffer */
4149
4150     /* !!! CHECK AGAIN !!! */
4151     SetPanelBackground();
4152     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4153     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4154   }
4155
4156   SetPanelBackground();
4157   SetDrawBackgroundMask(REDRAW_DOOR_1);
4158
4159   UpdateAndDisplayGameControlValues();
4160
4161   if (!game.restart_level)
4162   {
4163     UnmapGameButtons();
4164     UnmapTapeButtons();
4165
4166     FreeGameButtons();
4167     CreateGameButtons();
4168
4169     MapGameButtons();
4170     MapTapeButtons();
4171
4172     /* copy actual game door content to door double buffer for OpenDoor() */
4173     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4174
4175     OpenDoor(DOOR_OPEN_ALL);
4176
4177     KeyboardAutoRepeatOffUnlessAutoplay();
4178
4179 #if DEBUG_INIT_PLAYER
4180     DebugPrintPlayerStatus("Player status (final)");
4181 #endif
4182   }
4183
4184   UnmapAllGadgets();
4185
4186   MapGameButtons();
4187   MapTapeButtons();
4188
4189   if (!game.restart_level && !tape.playing)
4190   {
4191     LevelStats_incPlayed(level_nr);
4192
4193     SaveLevelSetup_SeriesInfo();
4194   }
4195
4196   game.restart_level = FALSE;
4197   game.restart_game_message = NULL;
4198
4199   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4200     InitGameActions_MM();
4201
4202   SaveEngineSnapshotToListInitial();
4203
4204   if (!game.restart_level)
4205   {
4206     PlaySound(SND_GAME_STARTING);
4207
4208     if (setup.sound_music)
4209       PlayLevelMusic();
4210   }
4211 }
4212
4213 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4214                         int actual_player_x, int actual_player_y)
4215 {
4216   /* this is used for non-R'n'D game engines to update certain engine values */
4217
4218   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4219   {
4220     actual_player_x = correctLevelPosX_EM(actual_player_x);
4221     actual_player_y = correctLevelPosY_EM(actual_player_y);
4222   }
4223
4224   /* needed to determine if sounds are played within the visible screen area */
4225   scroll_x = actual_scroll_x;
4226   scroll_y = actual_scroll_y;
4227
4228   /* needed to get player position for "follow finger" playing input method */
4229   local_player->jx = actual_player_x;
4230   local_player->jy = actual_player_y;
4231 }
4232
4233 void InitMovDir(int x, int y)
4234 {
4235   int i, element = Feld[x][y];
4236   static int xy[4][2] =
4237   {
4238     {  0, +1 },
4239     { +1,  0 },
4240     {  0, -1 },
4241     { -1,  0 }
4242   };
4243   static int direction[3][4] =
4244   {
4245     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4246     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4247     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4248   };
4249
4250   switch (element)
4251   {
4252     case EL_BUG_RIGHT:
4253     case EL_BUG_UP:
4254     case EL_BUG_LEFT:
4255     case EL_BUG_DOWN:
4256       Feld[x][y] = EL_BUG;
4257       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4258       break;
4259
4260     case EL_SPACESHIP_RIGHT:
4261     case EL_SPACESHIP_UP:
4262     case EL_SPACESHIP_LEFT:
4263     case EL_SPACESHIP_DOWN:
4264       Feld[x][y] = EL_SPACESHIP;
4265       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4266       break;
4267
4268     case EL_BD_BUTTERFLY_RIGHT:
4269     case EL_BD_BUTTERFLY_UP:
4270     case EL_BD_BUTTERFLY_LEFT:
4271     case EL_BD_BUTTERFLY_DOWN:
4272       Feld[x][y] = EL_BD_BUTTERFLY;
4273       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4274       break;
4275
4276     case EL_BD_FIREFLY_RIGHT:
4277     case EL_BD_FIREFLY_UP:
4278     case EL_BD_FIREFLY_LEFT:
4279     case EL_BD_FIREFLY_DOWN:
4280       Feld[x][y] = EL_BD_FIREFLY;
4281       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4282       break;
4283
4284     case EL_PACMAN_RIGHT:
4285     case EL_PACMAN_UP:
4286     case EL_PACMAN_LEFT:
4287     case EL_PACMAN_DOWN:
4288       Feld[x][y] = EL_PACMAN;
4289       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4290       break;
4291
4292     case EL_YAMYAM_LEFT:
4293     case EL_YAMYAM_RIGHT:
4294     case EL_YAMYAM_UP:
4295     case EL_YAMYAM_DOWN:
4296       Feld[x][y] = EL_YAMYAM;
4297       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4298       break;
4299
4300     case EL_SP_SNIKSNAK:
4301       MovDir[x][y] = MV_UP;
4302       break;
4303
4304     case EL_SP_ELECTRON:
4305       MovDir[x][y] = MV_LEFT;
4306       break;
4307
4308     case EL_MOLE_LEFT:
4309     case EL_MOLE_RIGHT:
4310     case EL_MOLE_UP:
4311     case EL_MOLE_DOWN:
4312       Feld[x][y] = EL_MOLE;
4313       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4314       break;
4315
4316     default:
4317       if (IS_CUSTOM_ELEMENT(element))
4318       {
4319         struct ElementInfo *ei = &element_info[element];
4320         int move_direction_initial = ei->move_direction_initial;
4321         int move_pattern = ei->move_pattern;
4322
4323         if (move_direction_initial == MV_START_PREVIOUS)
4324         {
4325           if (MovDir[x][y] != MV_NONE)
4326             return;
4327
4328           move_direction_initial = MV_START_AUTOMATIC;
4329         }
4330
4331         if (move_direction_initial == MV_START_RANDOM)
4332           MovDir[x][y] = 1 << RND(4);
4333         else if (move_direction_initial & MV_ANY_DIRECTION)
4334           MovDir[x][y] = move_direction_initial;
4335         else if (move_pattern == MV_ALL_DIRECTIONS ||
4336                  move_pattern == MV_TURNING_LEFT ||
4337                  move_pattern == MV_TURNING_RIGHT ||
4338                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4339                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4340                  move_pattern == MV_TURNING_RANDOM)
4341           MovDir[x][y] = 1 << RND(4);
4342         else if (move_pattern == MV_HORIZONTAL)
4343           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4344         else if (move_pattern == MV_VERTICAL)
4345           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4346         else if (move_pattern & MV_ANY_DIRECTION)
4347           MovDir[x][y] = element_info[element].move_pattern;
4348         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4349                  move_pattern == MV_ALONG_RIGHT_SIDE)
4350         {
4351           /* use random direction as default start direction */
4352           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4353             MovDir[x][y] = 1 << RND(4);
4354
4355           for (i = 0; i < NUM_DIRECTIONS; i++)
4356           {
4357             int x1 = x + xy[i][0];
4358             int y1 = y + xy[i][1];
4359
4360             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4361             {
4362               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4363                 MovDir[x][y] = direction[0][i];
4364               else
4365                 MovDir[x][y] = direction[1][i];
4366
4367               break;
4368             }
4369           }
4370         }                
4371       }
4372       else
4373       {
4374         MovDir[x][y] = 1 << RND(4);
4375
4376         if (element != EL_BUG &&
4377             element != EL_SPACESHIP &&
4378             element != EL_BD_BUTTERFLY &&
4379             element != EL_BD_FIREFLY)
4380           break;
4381
4382         for (i = 0; i < NUM_DIRECTIONS; i++)
4383         {
4384           int x1 = x + xy[i][0];
4385           int y1 = y + xy[i][1];
4386
4387           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4388           {
4389             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4390             {
4391               MovDir[x][y] = direction[0][i];
4392               break;
4393             }
4394             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4395                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4396             {
4397               MovDir[x][y] = direction[1][i];
4398               break;
4399             }
4400           }
4401         }
4402       }
4403       break;
4404   }
4405
4406   GfxDir[x][y] = MovDir[x][y];
4407 }
4408
4409 void InitAmoebaNr(int x, int y)
4410 {
4411   int i;
4412   int group_nr = AmoebeNachbarNr(x, y);
4413
4414   if (group_nr == 0)
4415   {
4416     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4417     {
4418       if (AmoebaCnt[i] == 0)
4419       {
4420         group_nr = i;
4421         break;
4422       }
4423     }
4424   }
4425
4426   AmoebaNr[x][y] = group_nr;
4427   AmoebaCnt[group_nr]++;
4428   AmoebaCnt2[group_nr]++;
4429 }
4430
4431 static void PlayerWins(struct PlayerInfo *player)
4432 {
4433   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4434       local_player->players_still_needed > 0)
4435     return;
4436
4437   player->LevelSolved = TRUE;
4438   player->GameOver = TRUE;
4439
4440   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4441                          level.native_em_level->lev->score :
4442                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4443                          game_mm.score :
4444                          player->score);
4445   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4446                           MM_HEALTH(game_mm.laser_overload_value) :
4447                           player->health);
4448
4449   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4450                                       TimeLeft);
4451   player->LevelSolved_CountingScore = player->score_final;
4452   player->LevelSolved_CountingHealth = player->health_final;
4453 }
4454
4455 void GameWon(void)
4456 {
4457   static int time_count_steps;
4458   static int time, time_final;
4459   static int score, score_final;
4460   static int health, health_final;
4461   static int game_over_delay_1 = 0;
4462   static int game_over_delay_2 = 0;
4463   static int game_over_delay_3 = 0;
4464   int game_over_delay_value_1 = 50;
4465   int game_over_delay_value_2 = 25;
4466   int game_over_delay_value_3 = 50;
4467
4468   if (!local_player->LevelSolved_GameWon)
4469   {
4470     int i;
4471
4472     /* do not start end game actions before the player stops moving (to exit) */
4473     if (local_player->MovPos)
4474       return;
4475
4476     local_player->LevelSolved_GameWon = TRUE;
4477     local_player->LevelSolved_SaveTape = tape.recording;
4478     local_player->LevelSolved_SaveScore = !tape.playing;
4479
4480     if (!tape.playing)
4481     {
4482       LevelStats_incSolved(level_nr);
4483
4484       SaveLevelSetup_SeriesInfo();
4485     }
4486
4487     if (tape.auto_play)         /* tape might already be stopped here */
4488       tape.auto_play_level_solved = TRUE;
4489
4490     TapeStop();
4491
4492     game_over_delay_1 = 0;
4493     game_over_delay_2 = 0;
4494     game_over_delay_3 = game_over_delay_value_3;
4495
4496     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4497     score = score_final = local_player->score_final;
4498     health = health_final = local_player->health_final;
4499
4500     if (level.score[SC_TIME_BONUS] > 0)
4501     {
4502       if (TimeLeft > 0)
4503       {
4504         time_final = 0;
4505         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4506       }
4507       else if (game.no_time_limit && TimePlayed < 999)
4508       {
4509         time_final = 999;
4510         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4511       }
4512
4513       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4514
4515       game_over_delay_1 = game_over_delay_value_1;
4516
4517       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4518       {
4519         health_final = 0;
4520         score_final += health * level.score[SC_TIME_BONUS];
4521
4522         game_over_delay_2 = game_over_delay_value_2;
4523       }
4524
4525       local_player->score_final = score_final;
4526       local_player->health_final = health_final;
4527     }
4528
4529     if (level_editor_test_game)
4530     {
4531       time = time_final;
4532       score = score_final;
4533
4534       local_player->LevelSolved_CountingTime = time;
4535       local_player->LevelSolved_CountingScore = score;
4536
4537       game_panel_controls[GAME_PANEL_TIME].value = time;
4538       game_panel_controls[GAME_PANEL_SCORE].value = score;
4539
4540       DisplayGameControlValues();
4541     }
4542
4543     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4544     {
4545       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4546       {
4547         /* close exit door after last player */
4548         if ((AllPlayersGone &&
4549              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4550               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4551               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4552             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4553             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4554         {
4555           int element = Feld[ExitX][ExitY];
4556
4557           Feld[ExitX][ExitY] =
4558             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4559              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4560              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4561              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4562              EL_EM_STEEL_EXIT_CLOSING);
4563
4564           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4565         }
4566
4567         /* player disappears */
4568         DrawLevelField(ExitX, ExitY);
4569       }
4570
4571       for (i = 0; i < MAX_PLAYERS; i++)
4572       {
4573         struct PlayerInfo *player = &stored_player[i];
4574
4575         if (player->present)
4576         {
4577           RemovePlayer(player);
4578
4579           /* player disappears */
4580           DrawLevelField(player->jx, player->jy);
4581         }
4582       }
4583     }
4584
4585     PlaySound(SND_GAME_WINNING);
4586   }
4587
4588   if (game_over_delay_1 > 0)
4589   {
4590     game_over_delay_1--;
4591
4592     return;
4593   }
4594
4595   if (time != time_final)
4596   {
4597     int time_to_go = ABS(time_final - time);
4598     int time_count_dir = (time < time_final ? +1 : -1);
4599
4600     if (time_to_go < time_count_steps)
4601       time_count_steps = 1;
4602
4603     time  += time_count_steps * time_count_dir;
4604     score += time_count_steps * level.score[SC_TIME_BONUS];
4605
4606     local_player->LevelSolved_CountingTime = time;
4607     local_player->LevelSolved_CountingScore = score;
4608
4609     game_panel_controls[GAME_PANEL_TIME].value = time;
4610     game_panel_controls[GAME_PANEL_SCORE].value = score;
4611
4612     DisplayGameControlValues();
4613
4614     if (time == time_final)
4615       StopSound(SND_GAME_LEVELTIME_BONUS);
4616     else if (setup.sound_loops)
4617       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4618     else
4619       PlaySound(SND_GAME_LEVELTIME_BONUS);
4620
4621     return;
4622   }
4623
4624   if (game_over_delay_2 > 0)
4625   {
4626     game_over_delay_2--;
4627
4628     return;
4629   }
4630
4631   if (health != health_final)
4632   {
4633     int health_count_dir = (health < health_final ? +1 : -1);
4634
4635     health += health_count_dir;
4636     score  += level.score[SC_TIME_BONUS];
4637
4638     local_player->LevelSolved_CountingHealth = health;
4639     local_player->LevelSolved_CountingScore = score;
4640
4641     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4642     game_panel_controls[GAME_PANEL_SCORE].value = score;
4643
4644     DisplayGameControlValues();
4645
4646     if (health == health_final)
4647       StopSound(SND_GAME_LEVELTIME_BONUS);
4648     else if (setup.sound_loops)
4649       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4650     else
4651       PlaySound(SND_GAME_LEVELTIME_BONUS);
4652
4653     return;
4654   }
4655
4656   local_player->LevelSolved_PanelOff = TRUE;
4657
4658   if (game_over_delay_3 > 0)
4659   {
4660     game_over_delay_3--;
4661
4662     return;
4663   }
4664
4665   GameEnd();
4666 }
4667
4668 void GameEnd(void)
4669 {
4670   /* used instead of "level_nr" (needed for network games) */
4671   int last_level_nr = levelset.level_nr;
4672   int hi_pos;
4673
4674   local_player->LevelSolved_GameEnd = TRUE;
4675
4676   if (local_player->LevelSolved_SaveTape)
4677   {
4678     /* make sure that request dialog to save tape does not open door again */
4679     if (!global.use_envelope_request)
4680       CloseDoor(DOOR_CLOSE_1);
4681
4682     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4683   }
4684
4685   /* if no tape is to be saved, close both doors simultaneously */
4686   CloseDoor(DOOR_CLOSE_ALL);
4687
4688   if (level_editor_test_game)
4689   {
4690     SetGameStatus(GAME_MODE_MAIN);
4691
4692     DrawMainMenu();
4693
4694     return;
4695   }
4696
4697   if (!local_player->LevelSolved_SaveScore)
4698   {
4699     SetGameStatus(GAME_MODE_MAIN);
4700
4701     DrawMainMenu();
4702
4703     return;
4704   }
4705
4706   if (level_nr == leveldir_current->handicap_level)
4707   {
4708     leveldir_current->handicap_level++;
4709
4710     SaveLevelSetup_SeriesInfo();
4711   }
4712
4713   if (setup.increment_levels &&
4714       level_nr < leveldir_current->last_level &&
4715       !network_playing)
4716   {
4717     level_nr++;         /* advance to next level */
4718     TapeErase();        /* start with empty tape */
4719
4720     if (setup.auto_play_next_level)
4721     {
4722       LoadLevel(level_nr);
4723
4724       SaveLevelSetup_SeriesInfo();
4725     }
4726   }
4727
4728   hi_pos = NewHiScore(last_level_nr);
4729
4730   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4731   {
4732     SetGameStatus(GAME_MODE_SCORES);
4733
4734     DrawHallOfFame(last_level_nr, hi_pos);
4735   }
4736   else if (setup.auto_play_next_level && setup.increment_levels &&
4737            !network_playing)
4738   {
4739     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4740   }
4741   else
4742   {
4743     SetGameStatus(GAME_MODE_MAIN);
4744
4745     DrawMainMenu();
4746   }
4747 }
4748
4749 int NewHiScore(int level_nr)
4750 {
4751   int k, l;
4752   int position = -1;
4753   boolean one_score_entry_per_name = !program.many_scores_per_name;
4754
4755   LoadScore(level_nr);
4756
4757   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4758       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4759     return -1;
4760
4761   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4762   {
4763     if (local_player->score_final > highscore[k].Score)
4764     {
4765       /* player has made it to the hall of fame */
4766
4767       if (k < MAX_SCORE_ENTRIES - 1)
4768       {
4769         int m = MAX_SCORE_ENTRIES - 1;
4770
4771         if (one_score_entry_per_name)
4772         {
4773           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4774             if (strEqual(setup.player_name, highscore[l].Name))
4775               m = l;
4776
4777           if (m == k)   /* player's new highscore overwrites his old one */
4778             goto put_into_list;
4779         }
4780
4781         for (l = m; l > k; l--)
4782         {
4783           strcpy(highscore[l].Name, highscore[l - 1].Name);
4784           highscore[l].Score = highscore[l - 1].Score;
4785         }
4786       }
4787
4788       put_into_list:
4789
4790       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4791       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4792       highscore[k].Score = local_player->score_final; 
4793       position = k;
4794
4795       break;
4796     }
4797     else if (one_score_entry_per_name &&
4798              !strncmp(setup.player_name, highscore[k].Name,
4799                       MAX_PLAYER_NAME_LEN))
4800       break;    /* player already there with a higher score */
4801   }
4802
4803   if (position >= 0) 
4804     SaveScore(level_nr);
4805
4806   return position;
4807 }
4808
4809 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4810 {
4811   int element = Feld[x][y];
4812   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4813   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4814   int horiz_move = (dx != 0);
4815   int sign = (horiz_move ? dx : dy);
4816   int step = sign * element_info[element].move_stepsize;
4817
4818   /* special values for move stepsize for spring and things on conveyor belt */
4819   if (horiz_move)
4820   {
4821     if (CAN_FALL(element) &&
4822         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4823       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4824     else if (element == EL_SPRING)
4825       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4826   }
4827
4828   return step;
4829 }
4830
4831 inline static int getElementMoveStepsize(int x, int y)
4832 {
4833   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4834 }
4835
4836 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4837 {
4838   if (player->GfxAction != action || player->GfxDir != dir)
4839   {
4840     player->GfxAction = action;
4841     player->GfxDir = dir;
4842     player->Frame = 0;
4843     player->StepFrame = 0;
4844   }
4845 }
4846
4847 static void ResetGfxFrame(int x, int y)
4848 {
4849   // profiling showed that "autotest" spends 10~20% of its time in this function
4850   if (DrawingDeactivatedField())
4851     return;
4852
4853   int element = Feld[x][y];
4854   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4855
4856   if (graphic_info[graphic].anim_global_sync)
4857     GfxFrame[x][y] = FrameCounter;
4858   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4859     GfxFrame[x][y] = CustomValue[x][y];
4860   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4861     GfxFrame[x][y] = element_info[element].collect_score;
4862   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4863     GfxFrame[x][y] = ChangeDelay[x][y];
4864 }
4865
4866 static void ResetGfxAnimation(int x, int y)
4867 {
4868   GfxAction[x][y] = ACTION_DEFAULT;
4869   GfxDir[x][y] = MovDir[x][y];
4870   GfxFrame[x][y] = 0;
4871
4872   ResetGfxFrame(x, y);
4873 }
4874
4875 static void ResetRandomAnimationValue(int x, int y)
4876 {
4877   GfxRandom[x][y] = INIT_GFX_RANDOM();
4878 }
4879
4880 static void InitMovingField(int x, int y, int direction)
4881 {
4882   int element = Feld[x][y];
4883   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4884   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4885   int newx = x + dx;
4886   int newy = y + dy;
4887   boolean is_moving_before, is_moving_after;
4888
4889   /* check if element was/is moving or being moved before/after mode change */
4890   is_moving_before = (WasJustMoving[x][y] != 0);
4891   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4892
4893   /* reset animation only for moving elements which change direction of moving
4894      or which just started or stopped moving
4895      (else CEs with property "can move" / "not moving" are reset each frame) */
4896   if (is_moving_before != is_moving_after ||
4897       direction != MovDir[x][y])
4898     ResetGfxAnimation(x, y);
4899
4900   MovDir[x][y] = direction;
4901   GfxDir[x][y] = direction;
4902
4903   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4904                      direction == MV_DOWN && CAN_FALL(element) ?
4905                      ACTION_FALLING : ACTION_MOVING);
4906
4907   /* this is needed for CEs with property "can move" / "not moving" */
4908
4909   if (is_moving_after)
4910   {
4911     if (Feld[newx][newy] == EL_EMPTY)
4912       Feld[newx][newy] = EL_BLOCKED;
4913
4914     MovDir[newx][newy] = MovDir[x][y];
4915
4916     CustomValue[newx][newy] = CustomValue[x][y];
4917
4918     GfxFrame[newx][newy] = GfxFrame[x][y];
4919     GfxRandom[newx][newy] = GfxRandom[x][y];
4920     GfxAction[newx][newy] = GfxAction[x][y];
4921     GfxDir[newx][newy] = GfxDir[x][y];
4922   }
4923 }
4924
4925 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4926 {
4927   int direction = MovDir[x][y];
4928   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4929   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4930
4931   *goes_to_x = newx;
4932   *goes_to_y = newy;
4933 }
4934
4935 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4936 {
4937   int oldx = x, oldy = y;
4938   int direction = MovDir[x][y];
4939
4940   if (direction == MV_LEFT)
4941     oldx++;
4942   else if (direction == MV_RIGHT)
4943     oldx--;
4944   else if (direction == MV_UP)
4945     oldy++;
4946   else if (direction == MV_DOWN)
4947     oldy--;
4948
4949   *comes_from_x = oldx;
4950   *comes_from_y = oldy;
4951 }
4952
4953 static int MovingOrBlocked2Element(int x, int y)
4954 {
4955   int element = Feld[x][y];
4956
4957   if (element == EL_BLOCKED)
4958   {
4959     int oldx, oldy;
4960
4961     Blocked2Moving(x, y, &oldx, &oldy);
4962     return Feld[oldx][oldy];
4963   }
4964   else
4965     return element;
4966 }
4967
4968 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4969 {
4970   /* like MovingOrBlocked2Element(), but if element is moving
4971      and (x,y) is the field the moving element is just leaving,
4972      return EL_BLOCKED instead of the element value */
4973   int element = Feld[x][y];
4974
4975   if (IS_MOVING(x, y))
4976   {
4977     if (element == EL_BLOCKED)
4978     {
4979       int oldx, oldy;
4980
4981       Blocked2Moving(x, y, &oldx, &oldy);
4982       return Feld[oldx][oldy];
4983     }
4984     else
4985       return EL_BLOCKED;
4986   }
4987   else
4988     return element;
4989 }
4990
4991 static void RemoveField(int x, int y)
4992 {
4993   Feld[x][y] = EL_EMPTY;
4994
4995   MovPos[x][y] = 0;
4996   MovDir[x][y] = 0;
4997   MovDelay[x][y] = 0;
4998
4999   CustomValue[x][y] = 0;
5000
5001   AmoebaNr[x][y] = 0;
5002   ChangeDelay[x][y] = 0;
5003   ChangePage[x][y] = -1;
5004   Pushed[x][y] = FALSE;
5005
5006   GfxElement[x][y] = EL_UNDEFINED;
5007   GfxAction[x][y] = ACTION_DEFAULT;
5008   GfxDir[x][y] = MV_NONE;
5009 }
5010
5011 static void RemoveMovingField(int x, int y)
5012 {
5013   int oldx = x, oldy = y, newx = x, newy = y;
5014   int element = Feld[x][y];
5015   int next_element = EL_UNDEFINED;
5016
5017   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5018     return;
5019
5020   if (IS_MOVING(x, y))
5021   {
5022     Moving2Blocked(x, y, &newx, &newy);
5023
5024     if (Feld[newx][newy] != EL_BLOCKED)
5025     {
5026       /* element is moving, but target field is not free (blocked), but
5027          already occupied by something different (example: acid pool);
5028          in this case, only remove the moving field, but not the target */
5029
5030       RemoveField(oldx, oldy);
5031
5032       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5033
5034       TEST_DrawLevelField(oldx, oldy);
5035
5036       return;
5037     }
5038   }
5039   else if (element == EL_BLOCKED)
5040   {
5041     Blocked2Moving(x, y, &oldx, &oldy);
5042     if (!IS_MOVING(oldx, oldy))
5043       return;
5044   }
5045
5046   if (element == EL_BLOCKED &&
5047       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5048        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5049        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5050        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5051        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5052        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5053     next_element = get_next_element(Feld[oldx][oldy]);
5054
5055   RemoveField(oldx, oldy);
5056   RemoveField(newx, newy);
5057
5058   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5059
5060   if (next_element != EL_UNDEFINED)
5061     Feld[oldx][oldy] = next_element;
5062
5063   TEST_DrawLevelField(oldx, oldy);
5064   TEST_DrawLevelField(newx, newy);
5065 }
5066
5067 void DrawDynamite(int x, int y)
5068 {
5069   int sx = SCREENX(x), sy = SCREENY(y);
5070   int graphic = el2img(Feld[x][y]);
5071   int frame;
5072
5073   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5074     return;
5075
5076   if (IS_WALKABLE_INSIDE(Back[x][y]))
5077     return;
5078
5079   if (Back[x][y])
5080     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5081   else if (Store[x][y])
5082     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5083
5084   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5085
5086   if (Back[x][y] || Store[x][y])
5087     DrawGraphicThruMask(sx, sy, graphic, frame);
5088   else
5089     DrawGraphic(sx, sy, graphic, frame);
5090 }
5091
5092 static void CheckDynamite(int x, int y)
5093 {
5094   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5095   {
5096     MovDelay[x][y]--;
5097
5098     if (MovDelay[x][y] != 0)
5099     {
5100       DrawDynamite(x, y);
5101       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5102
5103       return;
5104     }
5105   }
5106
5107   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5108
5109   Bang(x, y);
5110 }
5111
5112 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5113 {
5114   boolean num_checked_players = 0;
5115   int i;
5116
5117   for (i = 0; i < MAX_PLAYERS; i++)
5118   {
5119     if (stored_player[i].active)
5120     {
5121       int sx = stored_player[i].jx;
5122       int sy = stored_player[i].jy;
5123
5124       if (num_checked_players == 0)
5125       {
5126         *sx1 = *sx2 = sx;
5127         *sy1 = *sy2 = sy;
5128       }
5129       else
5130       {
5131         *sx1 = MIN(*sx1, sx);
5132         *sy1 = MIN(*sy1, sy);
5133         *sx2 = MAX(*sx2, sx);
5134         *sy2 = MAX(*sy2, sy);
5135       }
5136
5137       num_checked_players++;
5138     }
5139   }
5140 }
5141
5142 static boolean checkIfAllPlayersFitToScreen_RND(void)
5143 {
5144   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5145
5146   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5147
5148   return (sx2 - sx1 < SCR_FIELDX &&
5149           sy2 - sy1 < SCR_FIELDY);
5150 }
5151
5152 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5153 {
5154   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5155
5156   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5157
5158   *sx = (sx1 + sx2) / 2;
5159   *sy = (sy1 + sy2) / 2;
5160 }
5161
5162 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5163                                boolean center_screen, boolean quick_relocation)
5164 {
5165   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5166   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5167   boolean no_delay = (tape.warp_forward);
5168   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5169   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5170   int new_scroll_x, new_scroll_y;
5171
5172   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5173   {
5174     /* case 1: quick relocation inside visible screen (without scrolling) */
5175
5176     RedrawPlayfield();
5177
5178     return;
5179   }
5180
5181   if (!level.shifted_relocation || center_screen)
5182   {
5183     /* relocation _with_ centering of screen */
5184
5185     new_scroll_x = SCROLL_POSITION_X(x);
5186     new_scroll_y = SCROLL_POSITION_Y(y);
5187   }
5188   else
5189   {
5190     /* relocation _without_ centering of screen */
5191
5192     int center_scroll_x = SCROLL_POSITION_X(old_x);
5193     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5194     int offset_x = x + (scroll_x - center_scroll_x);
5195     int offset_y = y + (scroll_y - center_scroll_y);
5196
5197     /* for new screen position, apply previous offset to center position */
5198     new_scroll_x = SCROLL_POSITION_X(offset_x);
5199     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5200   }
5201
5202   if (quick_relocation)
5203   {
5204     /* case 2: quick relocation (redraw without visible scrolling) */
5205
5206     scroll_x = new_scroll_x;
5207     scroll_y = new_scroll_y;
5208
5209     RedrawPlayfield();
5210
5211     return;
5212   }
5213
5214   /* case 3: visible relocation (with scrolling to new position) */
5215
5216   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5217
5218   SetVideoFrameDelay(wait_delay_value);
5219
5220   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5221   {
5222     int dx = 0, dy = 0;
5223     int fx = FX, fy = FY;
5224
5225     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5226     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5227
5228     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5229       break;
5230
5231     scroll_x -= dx;
5232     scroll_y -= dy;
5233
5234     fx += dx * TILEX / 2;
5235     fy += dy * TILEY / 2;
5236
5237     ScrollLevel(dx, dy);
5238     DrawAllPlayers();
5239
5240     /* scroll in two steps of half tile size to make things smoother */
5241     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5242
5243     /* scroll second step to align at full tile size */
5244     BlitScreenToBitmap(window);
5245   }
5246
5247   DrawAllPlayers();
5248   BackToFront();
5249
5250   SetVideoFrameDelay(frame_delay_value_old);
5251 }
5252
5253 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5254 {
5255   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5256   int player_nr = GET_PLAYER_NR(el_player);
5257   struct PlayerInfo *player = &stored_player[player_nr];
5258   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5259   boolean no_delay = (tape.warp_forward);
5260   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5261   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5262   int old_jx = player->jx;
5263   int old_jy = player->jy;
5264   int old_element = Feld[old_jx][old_jy];
5265   int element = Feld[jx][jy];
5266   boolean player_relocated = (old_jx != jx || old_jy != jy);
5267
5268   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5269   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5270   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5271   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5272   int leave_side_horiz = move_dir_horiz;
5273   int leave_side_vert  = move_dir_vert;
5274   int enter_side = enter_side_horiz | enter_side_vert;
5275   int leave_side = leave_side_horiz | leave_side_vert;
5276
5277   if (player->GameOver)         /* do not reanimate dead player */
5278     return;
5279
5280   if (!player_relocated)        /* no need to relocate the player */
5281     return;
5282
5283   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5284   {
5285     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5286     DrawLevelField(jx, jy);
5287   }
5288
5289   if (player->present)
5290   {
5291     while (player->MovPos)
5292     {
5293       ScrollPlayer(player, SCROLL_GO_ON);
5294       ScrollScreen(NULL, SCROLL_GO_ON);
5295
5296       AdvanceFrameAndPlayerCounters(player->index_nr);
5297
5298       DrawPlayer(player);
5299
5300       BackToFront_WithFrameDelay(wait_delay_value);
5301     }
5302
5303     DrawPlayer(player);         /* needed here only to cleanup last field */
5304     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5305
5306     player->is_moving = FALSE;
5307   }
5308
5309   if (IS_CUSTOM_ELEMENT(old_element))
5310     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5311                                CE_LEFT_BY_PLAYER,
5312                                player->index_bit, leave_side);
5313
5314   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5315                                       CE_PLAYER_LEAVES_X,
5316                                       player->index_bit, leave_side);
5317
5318   Feld[jx][jy] = el_player;
5319   InitPlayerField(jx, jy, el_player, TRUE);
5320
5321   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5322      possible that the relocation target field did not contain a player element,
5323      but a walkable element, to which the new player was relocated -- in this
5324      case, restore that (already initialized!) element on the player field */
5325   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5326   {
5327     Feld[jx][jy] = element;     /* restore previously existing element */
5328   }
5329
5330   /* only visually relocate centered player */
5331   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5332                      FALSE, level.instant_relocation);
5333
5334   TestIfPlayerTouchesBadThing(jx, jy);
5335   TestIfPlayerTouchesCustomElement(jx, jy);
5336
5337   if (IS_CUSTOM_ELEMENT(element))
5338     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5339                                player->index_bit, enter_side);
5340
5341   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5342                                       player->index_bit, enter_side);
5343
5344   if (player->is_switching)
5345   {
5346     /* ensure that relocation while still switching an element does not cause
5347        a new element to be treated as also switched directly after relocation
5348        (this is important for teleporter switches that teleport the player to
5349        a place where another teleporter switch is in the same direction, which
5350        would then incorrectly be treated as immediately switched before the
5351        direction key that caused the switch was released) */
5352
5353     player->switch_x += jx - old_jx;
5354     player->switch_y += jy - old_jy;
5355   }
5356 }
5357
5358 static void Explode(int ex, int ey, int phase, int mode)
5359 {
5360   int x, y;
5361   int last_phase;
5362   int border_element;
5363
5364   /* !!! eliminate this variable !!! */
5365   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5366
5367   if (game.explosions_delayed)
5368   {
5369     ExplodeField[ex][ey] = mode;
5370     return;
5371   }
5372
5373   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5374   {
5375     int center_element = Feld[ex][ey];
5376     int artwork_element, explosion_element;     /* set these values later */
5377
5378     /* remove things displayed in background while burning dynamite */
5379     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5380       Back[ex][ey] = 0;
5381
5382     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5383     {
5384       /* put moving element to center field (and let it explode there) */
5385       center_element = MovingOrBlocked2Element(ex, ey);
5386       RemoveMovingField(ex, ey);
5387       Feld[ex][ey] = center_element;
5388     }
5389
5390     /* now "center_element" is finally determined -- set related values now */
5391     artwork_element = center_element;           /* for custom player artwork */
5392     explosion_element = center_element;         /* for custom player artwork */
5393
5394     if (IS_PLAYER(ex, ey))
5395     {
5396       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5397
5398       artwork_element = stored_player[player_nr].artwork_element;
5399
5400       if (level.use_explosion_element[player_nr])
5401       {
5402         explosion_element = level.explosion_element[player_nr];
5403         artwork_element = explosion_element;
5404       }
5405     }
5406
5407     if (mode == EX_TYPE_NORMAL ||
5408         mode == EX_TYPE_CENTER ||
5409         mode == EX_TYPE_CROSS)
5410       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5411
5412     last_phase = element_info[explosion_element].explosion_delay + 1;
5413
5414     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5415     {
5416       int xx = x - ex + 1;
5417       int yy = y - ey + 1;
5418       int element;
5419
5420       if (!IN_LEV_FIELD(x, y) ||
5421           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5422           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5423         continue;
5424
5425       element = Feld[x][y];
5426
5427       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5428       {
5429         element = MovingOrBlocked2Element(x, y);
5430
5431         if (!IS_EXPLOSION_PROOF(element))
5432           RemoveMovingField(x, y);
5433       }
5434
5435       /* indestructible elements can only explode in center (but not flames) */
5436       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5437                                            mode == EX_TYPE_BORDER)) ||
5438           element == EL_FLAMES)
5439         continue;
5440
5441       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5442          behaviour, for example when touching a yamyam that explodes to rocks
5443          with active deadly shield, a rock is created under the player !!! */
5444       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5445 #if 0
5446       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5447           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5448            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5449 #else
5450       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5451 #endif
5452       {
5453         if (IS_ACTIVE_BOMB(element))
5454         {
5455           /* re-activate things under the bomb like gate or penguin */
5456           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5457           Back[x][y] = 0;
5458         }
5459
5460         continue;
5461       }
5462
5463       /* save walkable background elements while explosion on same tile */
5464       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5465           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5466         Back[x][y] = element;
5467
5468       /* ignite explodable elements reached by other explosion */
5469       if (element == EL_EXPLOSION)
5470         element = Store2[x][y];
5471
5472       if (AmoebaNr[x][y] &&
5473           (element == EL_AMOEBA_FULL ||
5474            element == EL_BD_AMOEBA ||
5475            element == EL_AMOEBA_GROWING))
5476       {
5477         AmoebaCnt[AmoebaNr[x][y]]--;
5478         AmoebaCnt2[AmoebaNr[x][y]]--;
5479       }
5480
5481       RemoveField(x, y);
5482
5483       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5484       {
5485         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5486
5487         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5488
5489         if (PLAYERINFO(ex, ey)->use_murphy)
5490           Store[x][y] = EL_EMPTY;
5491       }
5492
5493       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5494          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5495       else if (ELEM_IS_PLAYER(center_element))
5496         Store[x][y] = EL_EMPTY;
5497       else if (center_element == EL_YAMYAM)
5498         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5499       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5500         Store[x][y] = element_info[center_element].content.e[xx][yy];
5501 #if 1
5502       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5503          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5504          otherwise) -- FIX THIS !!! */
5505       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5506         Store[x][y] = element_info[element].content.e[1][1];
5507 #else
5508       else if (!CAN_EXPLODE(element))
5509         Store[x][y] = element_info[element].content.e[1][1];
5510 #endif
5511       else
5512         Store[x][y] = EL_EMPTY;
5513
5514       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5515           center_element == EL_AMOEBA_TO_DIAMOND)
5516         Store2[x][y] = element;
5517
5518       Feld[x][y] = EL_EXPLOSION;
5519       GfxElement[x][y] = artwork_element;
5520
5521       ExplodePhase[x][y] = 1;
5522       ExplodeDelay[x][y] = last_phase;
5523
5524       Stop[x][y] = TRUE;
5525     }
5526
5527     if (center_element == EL_YAMYAM)
5528       game.yamyam_content_nr =
5529         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5530
5531     return;
5532   }
5533
5534   if (Stop[ex][ey])
5535     return;
5536
5537   x = ex;
5538   y = ey;
5539
5540   if (phase == 1)
5541     GfxFrame[x][y] = 0;         /* restart explosion animation */
5542
5543   last_phase = ExplodeDelay[x][y];
5544
5545   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5546
5547   /* this can happen if the player leaves an explosion just in time */
5548   if (GfxElement[x][y] == EL_UNDEFINED)
5549     GfxElement[x][y] = EL_EMPTY;
5550
5551   border_element = Store2[x][y];
5552   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5553     border_element = StorePlayer[x][y];
5554
5555   if (phase == element_info[border_element].ignition_delay ||
5556       phase == last_phase)
5557   {
5558     boolean border_explosion = FALSE;
5559
5560     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5561         !PLAYER_EXPLOSION_PROTECTED(x, y))
5562     {
5563       KillPlayerUnlessExplosionProtected(x, y);
5564       border_explosion = TRUE;
5565     }
5566     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5567     {
5568       Feld[x][y] = Store2[x][y];
5569       Store2[x][y] = 0;
5570       Bang(x, y);
5571       border_explosion = TRUE;
5572     }
5573     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5574     {
5575       AmoebeUmwandeln(x, y);
5576       Store2[x][y] = 0;
5577       border_explosion = TRUE;
5578     }
5579
5580     /* if an element just explodes due to another explosion (chain-reaction),
5581        do not immediately end the new explosion when it was the last frame of
5582        the explosion (as it would be done in the following "if"-statement!) */
5583     if (border_explosion && phase == last_phase)
5584       return;
5585   }
5586
5587   if (phase == last_phase)
5588   {
5589     int element;
5590
5591     element = Feld[x][y] = Store[x][y];
5592     Store[x][y] = Store2[x][y] = 0;
5593     GfxElement[x][y] = EL_UNDEFINED;
5594
5595     /* player can escape from explosions and might therefore be still alive */
5596     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5597         element <= EL_PLAYER_IS_EXPLODING_4)
5598     {
5599       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5600       int explosion_element = EL_PLAYER_1 + player_nr;
5601       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5602       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5603
5604       if (level.use_explosion_element[player_nr])
5605         explosion_element = level.explosion_element[player_nr];
5606
5607       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5608                     element_info[explosion_element].content.e[xx][yy]);
5609     }
5610
5611     /* restore probably existing indestructible background element */
5612     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5613       element = Feld[x][y] = Back[x][y];
5614     Back[x][y] = 0;
5615
5616     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5617     GfxDir[x][y] = MV_NONE;
5618     ChangeDelay[x][y] = 0;
5619     ChangePage[x][y] = -1;
5620
5621     CustomValue[x][y] = 0;
5622
5623     InitField_WithBug2(x, y, FALSE);
5624
5625     TEST_DrawLevelField(x, y);
5626
5627     TestIfElementTouchesCustomElement(x, y);
5628
5629     if (GFX_CRUMBLED(element))
5630       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5631
5632     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5633       StorePlayer[x][y] = 0;
5634
5635     if (ELEM_IS_PLAYER(element))
5636       RelocatePlayer(x, y, element);
5637   }
5638   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5639   {
5640     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5641     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5642
5643     if (phase == delay)
5644       TEST_DrawLevelFieldCrumbled(x, y);
5645
5646     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5647     {
5648       DrawLevelElement(x, y, Back[x][y]);
5649       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5650     }
5651     else if (IS_WALKABLE_UNDER(Back[x][y]))
5652     {
5653       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5654       DrawLevelElementThruMask(x, y, Back[x][y]);
5655     }
5656     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5657       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5658   }
5659 }
5660
5661 static void DynaExplode(int ex, int ey)
5662 {
5663   int i, j;
5664   int dynabomb_element = Feld[ex][ey];
5665   int dynabomb_size = 1;
5666   boolean dynabomb_xl = FALSE;
5667   struct PlayerInfo *player;
5668   static int xy[4][2] =
5669   {
5670     { 0, -1 },
5671     { -1, 0 },
5672     { +1, 0 },
5673     { 0, +1 }
5674   };
5675
5676   if (IS_ACTIVE_BOMB(dynabomb_element))
5677   {
5678     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5679     dynabomb_size = player->dynabomb_size;
5680     dynabomb_xl = player->dynabomb_xl;
5681     player->dynabombs_left++;
5682   }
5683
5684   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5685
5686   for (i = 0; i < NUM_DIRECTIONS; i++)
5687   {
5688     for (j = 1; j <= dynabomb_size; j++)
5689     {
5690       int x = ex + j * xy[i][0];
5691       int y = ey + j * xy[i][1];
5692       int element;
5693
5694       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5695         break;
5696
5697       element = Feld[x][y];
5698
5699       /* do not restart explosions of fields with active bombs */
5700       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5701         continue;
5702
5703       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5704
5705       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5706           !IS_DIGGABLE(element) && !dynabomb_xl)
5707         break;
5708     }
5709   }
5710 }
5711
5712 void Bang(int x, int y)
5713 {
5714   int element = MovingOrBlocked2Element(x, y);
5715   int explosion_type = EX_TYPE_NORMAL;
5716
5717   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5718   {
5719     struct PlayerInfo *player = PLAYERINFO(x, y);
5720
5721     element = Feld[x][y] = player->initial_element;
5722
5723     if (level.use_explosion_element[player->index_nr])
5724     {
5725       int explosion_element = level.explosion_element[player->index_nr];
5726
5727       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5728         explosion_type = EX_TYPE_CROSS;
5729       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5730         explosion_type = EX_TYPE_CENTER;
5731     }
5732   }
5733
5734   switch (element)
5735   {
5736     case EL_BUG:
5737     case EL_SPACESHIP:
5738     case EL_BD_BUTTERFLY:
5739     case EL_BD_FIREFLY:
5740     case EL_YAMYAM:
5741     case EL_DARK_YAMYAM:
5742     case EL_ROBOT:
5743     case EL_PACMAN:
5744     case EL_MOLE:
5745       RaiseScoreElement(element);
5746       break;
5747
5748     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5749     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5750     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5751     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5752     case EL_DYNABOMB_INCREASE_NUMBER:
5753     case EL_DYNABOMB_INCREASE_SIZE:
5754     case EL_DYNABOMB_INCREASE_POWER:
5755       explosion_type = EX_TYPE_DYNA;
5756       break;
5757
5758     case EL_DC_LANDMINE:
5759       explosion_type = EX_TYPE_CENTER;
5760       break;
5761
5762     case EL_PENGUIN:
5763     case EL_LAMP:
5764     case EL_LAMP_ACTIVE:
5765     case EL_AMOEBA_TO_DIAMOND:
5766       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5767         explosion_type = EX_TYPE_CENTER;
5768       break;
5769
5770     default:
5771       if (element_info[element].explosion_type == EXPLODES_CROSS)
5772         explosion_type = EX_TYPE_CROSS;
5773       else if (element_info[element].explosion_type == EXPLODES_1X1)
5774         explosion_type = EX_TYPE_CENTER;
5775       break;
5776   }
5777
5778   if (explosion_type == EX_TYPE_DYNA)
5779     DynaExplode(x, y);
5780   else
5781     Explode(x, y, EX_PHASE_START, explosion_type);
5782
5783   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5784 }
5785
5786 static void SplashAcid(int x, int y)
5787 {
5788   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5789       (!IN_LEV_FIELD(x - 1, y - 2) ||
5790        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5791     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5792
5793   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5794       (!IN_LEV_FIELD(x + 1, y - 2) ||
5795        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5796     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5797
5798   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5799 }
5800
5801 static void InitBeltMovement(void)
5802 {
5803   static int belt_base_element[4] =
5804   {
5805     EL_CONVEYOR_BELT_1_LEFT,
5806     EL_CONVEYOR_BELT_2_LEFT,
5807     EL_CONVEYOR_BELT_3_LEFT,
5808     EL_CONVEYOR_BELT_4_LEFT
5809   };
5810   static int belt_base_active_element[4] =
5811   {
5812     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5813     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5814     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5815     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5816   };
5817
5818   int x, y, i, j;
5819
5820   /* set frame order for belt animation graphic according to belt direction */
5821   for (i = 0; i < NUM_BELTS; i++)
5822   {
5823     int belt_nr = i;
5824
5825     for (j = 0; j < NUM_BELT_PARTS; j++)
5826     {
5827       int element = belt_base_active_element[belt_nr] + j;
5828       int graphic_1 = el2img(element);
5829       int graphic_2 = el2panelimg(element);
5830
5831       if (game.belt_dir[i] == MV_LEFT)
5832       {
5833         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5834         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5835       }
5836       else
5837       {
5838         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5839         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5840       }
5841     }
5842   }
5843
5844   SCAN_PLAYFIELD(x, y)
5845   {
5846     int element = Feld[x][y];
5847
5848     for (i = 0; i < NUM_BELTS; i++)
5849     {
5850       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5851       {
5852         int e_belt_nr = getBeltNrFromBeltElement(element);
5853         int belt_nr = i;
5854
5855         if (e_belt_nr == belt_nr)
5856         {
5857           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5858
5859           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5860         }
5861       }
5862     }
5863   }
5864 }
5865
5866 static void ToggleBeltSwitch(int x, int y)
5867 {
5868   static int belt_base_element[4] =
5869   {
5870     EL_CONVEYOR_BELT_1_LEFT,
5871     EL_CONVEYOR_BELT_2_LEFT,
5872     EL_CONVEYOR_BELT_3_LEFT,
5873     EL_CONVEYOR_BELT_4_LEFT
5874   };
5875   static int belt_base_active_element[4] =
5876   {
5877     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5878     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5879     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5880     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5881   };
5882   static int belt_base_switch_element[4] =
5883   {
5884     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5885     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5886     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5887     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5888   };
5889   static int belt_move_dir[4] =
5890   {
5891     MV_LEFT,
5892     MV_NONE,
5893     MV_RIGHT,
5894     MV_NONE,
5895   };
5896
5897   int element = Feld[x][y];
5898   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5899   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5900   int belt_dir = belt_move_dir[belt_dir_nr];
5901   int xx, yy, i;
5902
5903   if (!IS_BELT_SWITCH(element))
5904     return;
5905
5906   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5907   game.belt_dir[belt_nr] = belt_dir;
5908
5909   if (belt_dir_nr == 3)
5910     belt_dir_nr = 1;
5911
5912   /* set frame order for belt animation graphic according to belt direction */
5913   for (i = 0; i < NUM_BELT_PARTS; i++)
5914   {
5915     int element = belt_base_active_element[belt_nr] + i;
5916     int graphic_1 = el2img(element);
5917     int graphic_2 = el2panelimg(element);
5918
5919     if (belt_dir == MV_LEFT)
5920     {
5921       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5922       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5923     }
5924     else
5925     {
5926       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5927       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5928     }
5929   }
5930
5931   SCAN_PLAYFIELD(xx, yy)
5932   {
5933     int element = Feld[xx][yy];
5934
5935     if (IS_BELT_SWITCH(element))
5936     {
5937       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5938
5939       if (e_belt_nr == belt_nr)
5940       {
5941         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5942         TEST_DrawLevelField(xx, yy);
5943       }
5944     }
5945     else if (IS_BELT(element) && belt_dir != MV_NONE)
5946     {
5947       int e_belt_nr = getBeltNrFromBeltElement(element);
5948
5949       if (e_belt_nr == belt_nr)
5950       {
5951         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5952
5953         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5954         TEST_DrawLevelField(xx, yy);
5955       }
5956     }
5957     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5958     {
5959       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5960
5961       if (e_belt_nr == belt_nr)
5962       {
5963         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5964
5965         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5966         TEST_DrawLevelField(xx, yy);
5967       }
5968     }
5969   }
5970 }
5971
5972 static void ToggleSwitchgateSwitch(int x, int y)
5973 {
5974   int xx, yy;
5975
5976   game.switchgate_pos = !game.switchgate_pos;
5977
5978   SCAN_PLAYFIELD(xx, yy)
5979   {
5980     int element = Feld[xx][yy];
5981
5982     if (element == EL_SWITCHGATE_SWITCH_UP)
5983     {
5984       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5985       TEST_DrawLevelField(xx, yy);
5986     }
5987     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5988     {
5989       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5990       TEST_DrawLevelField(xx, yy);
5991     }
5992     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5993     {
5994       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5995       TEST_DrawLevelField(xx, yy);
5996     }
5997     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5998     {
5999       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6000       TEST_DrawLevelField(xx, yy);
6001     }
6002     else if (element == EL_SWITCHGATE_OPEN ||
6003              element == EL_SWITCHGATE_OPENING)
6004     {
6005       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6006
6007       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6008     }
6009     else if (element == EL_SWITCHGATE_CLOSED ||
6010              element == EL_SWITCHGATE_CLOSING)
6011     {
6012       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6013
6014       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6015     }
6016   }
6017 }
6018
6019 static int getInvisibleActiveFromInvisibleElement(int element)
6020 {
6021   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6022           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6023           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6024           element);
6025 }
6026
6027 static int getInvisibleFromInvisibleActiveElement(int element)
6028 {
6029   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6030           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6031           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6032           element);
6033 }
6034
6035 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6036 {
6037   int x, y;
6038
6039   SCAN_PLAYFIELD(x, y)
6040   {
6041     int element = Feld[x][y];
6042
6043     if (element == EL_LIGHT_SWITCH &&
6044         game.light_time_left > 0)
6045     {
6046       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6047       TEST_DrawLevelField(x, y);
6048     }
6049     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6050              game.light_time_left == 0)
6051     {
6052       Feld[x][y] = EL_LIGHT_SWITCH;
6053       TEST_DrawLevelField(x, y);
6054     }
6055     else if (element == EL_EMC_DRIPPER &&
6056              game.light_time_left > 0)
6057     {
6058       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6059       TEST_DrawLevelField(x, y);
6060     }
6061     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6062              game.light_time_left == 0)
6063     {
6064       Feld[x][y] = EL_EMC_DRIPPER;
6065       TEST_DrawLevelField(x, y);
6066     }
6067     else if (element == EL_INVISIBLE_STEELWALL ||
6068              element == EL_INVISIBLE_WALL ||
6069              element == EL_INVISIBLE_SAND)
6070     {
6071       if (game.light_time_left > 0)
6072         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6073
6074       TEST_DrawLevelField(x, y);
6075
6076       /* uncrumble neighbour fields, if needed */
6077       if (element == EL_INVISIBLE_SAND)
6078         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6079     }
6080     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6081              element == EL_INVISIBLE_WALL_ACTIVE ||
6082              element == EL_INVISIBLE_SAND_ACTIVE)
6083     {
6084       if (game.light_time_left == 0)
6085         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6086
6087       TEST_DrawLevelField(x, y);
6088
6089       /* re-crumble neighbour fields, if needed */
6090       if (element == EL_INVISIBLE_SAND)
6091         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6092     }
6093   }
6094 }
6095
6096 static void RedrawAllInvisibleElementsForLenses(void)
6097 {
6098   int x, y;
6099
6100   SCAN_PLAYFIELD(x, y)
6101   {
6102     int element = Feld[x][y];
6103
6104     if (element == EL_EMC_DRIPPER &&
6105         game.lenses_time_left > 0)
6106     {
6107       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6108       TEST_DrawLevelField(x, y);
6109     }
6110     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6111              game.lenses_time_left == 0)
6112     {
6113       Feld[x][y] = EL_EMC_DRIPPER;
6114       TEST_DrawLevelField(x, y);
6115     }
6116     else if (element == EL_INVISIBLE_STEELWALL ||
6117              element == EL_INVISIBLE_WALL ||
6118              element == EL_INVISIBLE_SAND)
6119     {
6120       if (game.lenses_time_left > 0)
6121         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6122
6123       TEST_DrawLevelField(x, y);
6124
6125       /* uncrumble neighbour fields, if needed */
6126       if (element == EL_INVISIBLE_SAND)
6127         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6128     }
6129     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6130              element == EL_INVISIBLE_WALL_ACTIVE ||
6131              element == EL_INVISIBLE_SAND_ACTIVE)
6132     {
6133       if (game.lenses_time_left == 0)
6134         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6135
6136       TEST_DrawLevelField(x, y);
6137
6138       /* re-crumble neighbour fields, if needed */
6139       if (element == EL_INVISIBLE_SAND)
6140         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6141     }
6142   }
6143 }
6144
6145 static void RedrawAllInvisibleElementsForMagnifier(void)
6146 {
6147   int x, y;
6148
6149   SCAN_PLAYFIELD(x, y)
6150   {
6151     int element = Feld[x][y];
6152
6153     if (element == EL_EMC_FAKE_GRASS &&
6154         game.magnify_time_left > 0)
6155     {
6156       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6157       TEST_DrawLevelField(x, y);
6158     }
6159     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6160              game.magnify_time_left == 0)
6161     {
6162       Feld[x][y] = EL_EMC_FAKE_GRASS;
6163       TEST_DrawLevelField(x, y);
6164     }
6165     else if (IS_GATE_GRAY(element) &&
6166              game.magnify_time_left > 0)
6167     {
6168       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6169                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6170                     IS_EM_GATE_GRAY(element) ?
6171                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6172                     IS_EMC_GATE_GRAY(element) ?
6173                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6174                     IS_DC_GATE_GRAY(element) ?
6175                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6176                     element);
6177       TEST_DrawLevelField(x, y);
6178     }
6179     else if (IS_GATE_GRAY_ACTIVE(element) &&
6180              game.magnify_time_left == 0)
6181     {
6182       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6183                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6184                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6185                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6186                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6187                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6188                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6189                     EL_DC_GATE_WHITE_GRAY :
6190                     element);
6191       TEST_DrawLevelField(x, y);
6192     }
6193   }
6194 }
6195
6196 static void ToggleLightSwitch(int x, int y)
6197 {
6198   int element = Feld[x][y];
6199
6200   game.light_time_left =
6201     (element == EL_LIGHT_SWITCH ?
6202      level.time_light * FRAMES_PER_SECOND : 0);
6203
6204   RedrawAllLightSwitchesAndInvisibleElements();
6205 }
6206
6207 static void ActivateTimegateSwitch(int x, int y)
6208 {
6209   int xx, yy;
6210
6211   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6212
6213   SCAN_PLAYFIELD(xx, yy)
6214   {
6215     int element = Feld[xx][yy];
6216
6217     if (element == EL_TIMEGATE_CLOSED ||
6218         element == EL_TIMEGATE_CLOSING)
6219     {
6220       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6221       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6222     }
6223
6224     /*
6225     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6226     {
6227       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6228       TEST_DrawLevelField(xx, yy);
6229     }
6230     */
6231
6232   }
6233
6234   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6235                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6236 }
6237
6238 static void Impact(int x, int y)
6239 {
6240   boolean last_line = (y == lev_fieldy - 1);
6241   boolean object_hit = FALSE;
6242   boolean impact = (last_line || object_hit);
6243   int element = Feld[x][y];
6244   int smashed = EL_STEELWALL;
6245
6246   if (!last_line)       /* check if element below was hit */
6247   {
6248     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6249       return;
6250
6251     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6252                                          MovDir[x][y + 1] != MV_DOWN ||
6253                                          MovPos[x][y + 1] <= TILEY / 2));
6254
6255     /* do not smash moving elements that left the smashed field in time */
6256     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6257         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6258       object_hit = FALSE;
6259
6260 #if USE_QUICKSAND_IMPACT_BUGFIX
6261     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6262     {
6263       RemoveMovingField(x, y + 1);
6264       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6265       Feld[x][y + 2] = EL_ROCK;
6266       TEST_DrawLevelField(x, y + 2);
6267
6268       object_hit = TRUE;
6269     }
6270
6271     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6272     {
6273       RemoveMovingField(x, y + 1);
6274       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6275       Feld[x][y + 2] = EL_ROCK;
6276       TEST_DrawLevelField(x, y + 2);
6277
6278       object_hit = TRUE;
6279     }
6280 #endif
6281
6282     if (object_hit)
6283       smashed = MovingOrBlocked2Element(x, y + 1);
6284
6285     impact = (last_line || object_hit);
6286   }
6287
6288   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6289   {
6290     SplashAcid(x, y + 1);
6291     return;
6292   }
6293
6294   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6295   /* only reset graphic animation if graphic really changes after impact */
6296   if (impact &&
6297       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6298   {
6299     ResetGfxAnimation(x, y);
6300     TEST_DrawLevelField(x, y);
6301   }
6302
6303   if (impact && CAN_EXPLODE_IMPACT(element))
6304   {
6305     Bang(x, y);
6306     return;
6307   }
6308   else if (impact && element == EL_PEARL &&
6309            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6310   {
6311     ResetGfxAnimation(x, y);
6312
6313     Feld[x][y] = EL_PEARL_BREAKING;
6314     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6315     return;
6316   }
6317   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6318   {
6319     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6320
6321     return;
6322   }
6323
6324   if (impact && element == EL_AMOEBA_DROP)
6325   {
6326     if (object_hit && IS_PLAYER(x, y + 1))
6327       KillPlayerUnlessEnemyProtected(x, y + 1);
6328     else if (object_hit && smashed == EL_PENGUIN)
6329       Bang(x, y + 1);
6330     else
6331     {
6332       Feld[x][y] = EL_AMOEBA_GROWING;
6333       Store[x][y] = EL_AMOEBA_WET;
6334
6335       ResetRandomAnimationValue(x, y);
6336     }
6337     return;
6338   }
6339
6340   if (object_hit)               /* check which object was hit */
6341   {
6342     if ((CAN_PASS_MAGIC_WALL(element) && 
6343          (smashed == EL_MAGIC_WALL ||
6344           smashed == EL_BD_MAGIC_WALL)) ||
6345         (CAN_PASS_DC_MAGIC_WALL(element) &&
6346          smashed == EL_DC_MAGIC_WALL))
6347     {
6348       int xx, yy;
6349       int activated_magic_wall =
6350         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6351          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6352          EL_DC_MAGIC_WALL_ACTIVE);
6353
6354       /* activate magic wall / mill */
6355       SCAN_PLAYFIELD(xx, yy)
6356       {
6357         if (Feld[xx][yy] == smashed)
6358           Feld[xx][yy] = activated_magic_wall;
6359       }
6360
6361       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6362       game.magic_wall_active = TRUE;
6363
6364       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6365                             SND_MAGIC_WALL_ACTIVATING :
6366                             smashed == EL_BD_MAGIC_WALL ?
6367                             SND_BD_MAGIC_WALL_ACTIVATING :
6368                             SND_DC_MAGIC_WALL_ACTIVATING));
6369     }
6370
6371     if (IS_PLAYER(x, y + 1))
6372     {
6373       if (CAN_SMASH_PLAYER(element))
6374       {
6375         KillPlayerUnlessEnemyProtected(x, y + 1);
6376         return;
6377       }
6378     }
6379     else if (smashed == EL_PENGUIN)
6380     {
6381       if (CAN_SMASH_PLAYER(element))
6382       {
6383         Bang(x, y + 1);
6384         return;
6385       }
6386     }
6387     else if (element == EL_BD_DIAMOND)
6388     {
6389       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6390       {
6391         Bang(x, y + 1);
6392         return;
6393       }
6394     }
6395     else if (((element == EL_SP_INFOTRON ||
6396                element == EL_SP_ZONK) &&
6397               (smashed == EL_SP_SNIKSNAK ||
6398                smashed == EL_SP_ELECTRON ||
6399                smashed == EL_SP_DISK_ORANGE)) ||
6400              (element == EL_SP_INFOTRON &&
6401               smashed == EL_SP_DISK_YELLOW))
6402     {
6403       Bang(x, y + 1);
6404       return;
6405     }
6406     else if (CAN_SMASH_EVERYTHING(element))
6407     {
6408       if (IS_CLASSIC_ENEMY(smashed) ||
6409           CAN_EXPLODE_SMASHED(smashed))
6410       {
6411         Bang(x, y + 1);
6412         return;
6413       }
6414       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6415       {
6416         if (smashed == EL_LAMP ||
6417             smashed == EL_LAMP_ACTIVE)
6418         {
6419           Bang(x, y + 1);
6420           return;
6421         }
6422         else if (smashed == EL_NUT)
6423         {
6424           Feld[x][y + 1] = EL_NUT_BREAKING;
6425           PlayLevelSound(x, y, SND_NUT_BREAKING);
6426           RaiseScoreElement(EL_NUT);
6427           return;
6428         }
6429         else if (smashed == EL_PEARL)
6430         {
6431           ResetGfxAnimation(x, y);
6432
6433           Feld[x][y + 1] = EL_PEARL_BREAKING;
6434           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6435           return;
6436         }
6437         else if (smashed == EL_DIAMOND)
6438         {
6439           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6440           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6441           return;
6442         }
6443         else if (IS_BELT_SWITCH(smashed))
6444         {
6445           ToggleBeltSwitch(x, y + 1);
6446         }
6447         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6448                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6449                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6450                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6451         {
6452           ToggleSwitchgateSwitch(x, y + 1);
6453         }
6454         else if (smashed == EL_LIGHT_SWITCH ||
6455                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6456         {
6457           ToggleLightSwitch(x, y + 1);
6458         }
6459         else
6460         {
6461           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6462
6463           CheckElementChangeBySide(x, y + 1, smashed, element,
6464                                    CE_SWITCHED, CH_SIDE_TOP);
6465           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6466                                             CH_SIDE_TOP);
6467         }
6468       }
6469       else
6470       {
6471         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6472       }
6473     }
6474   }
6475
6476   /* play sound of magic wall / mill */
6477   if (!last_line &&
6478       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6479        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6480        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6481   {
6482     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6483       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6484     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6485       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6486     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6487       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6488
6489     return;
6490   }
6491
6492   /* play sound of object that hits the ground */
6493   if (last_line || object_hit)
6494     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6495 }
6496
6497 inline static void TurnRoundExt(int x, int y)
6498 {
6499   static struct
6500   {
6501     int dx, dy;
6502   } move_xy[] =
6503   {
6504     {  0,  0 },
6505     { -1,  0 },
6506     { +1,  0 },
6507     {  0,  0 },
6508     {  0, -1 },
6509     {  0,  0 }, { 0, 0 }, { 0, 0 },
6510     {  0, +1 }
6511   };
6512   static struct
6513   {
6514     int left, right, back;
6515   } turn[] =
6516   {
6517     { 0,        0,              0        },
6518     { MV_DOWN,  MV_UP,          MV_RIGHT },
6519     { MV_UP,    MV_DOWN,        MV_LEFT  },
6520     { 0,        0,              0        },
6521     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6522     { 0,        0,              0        },
6523     { 0,        0,              0        },
6524     { 0,        0,              0        },
6525     { MV_RIGHT, MV_LEFT,        MV_UP    }
6526   };
6527
6528   int element = Feld[x][y];
6529   int move_pattern = element_info[element].move_pattern;
6530
6531   int old_move_dir = MovDir[x][y];
6532   int left_dir  = turn[old_move_dir].left;
6533   int right_dir = turn[old_move_dir].right;
6534   int back_dir  = turn[old_move_dir].back;
6535
6536   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6537   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6538   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6539   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6540
6541   int left_x  = x + left_dx,  left_y  = y + left_dy;
6542   int right_x = x + right_dx, right_y = y + right_dy;
6543   int move_x  = x + move_dx,  move_y  = y + move_dy;
6544
6545   int xx, yy;
6546
6547   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6548   {
6549     TestIfBadThingTouchesOtherBadThing(x, y);
6550
6551     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6552       MovDir[x][y] = right_dir;
6553     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6554       MovDir[x][y] = left_dir;
6555
6556     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6557       MovDelay[x][y] = 9;
6558     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6559       MovDelay[x][y] = 1;
6560   }
6561   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6562   {
6563     TestIfBadThingTouchesOtherBadThing(x, y);
6564
6565     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6566       MovDir[x][y] = left_dir;
6567     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6568       MovDir[x][y] = right_dir;
6569
6570     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6571       MovDelay[x][y] = 9;
6572     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6573       MovDelay[x][y] = 1;
6574   }
6575   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6576   {
6577     TestIfBadThingTouchesOtherBadThing(x, y);
6578
6579     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6580       MovDir[x][y] = left_dir;
6581     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6582       MovDir[x][y] = right_dir;
6583
6584     if (MovDir[x][y] != old_move_dir)
6585       MovDelay[x][y] = 9;
6586   }
6587   else if (element == EL_YAMYAM)
6588   {
6589     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6590     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6591
6592     if (can_turn_left && can_turn_right)
6593       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6594     else if (can_turn_left)
6595       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6596     else if (can_turn_right)
6597       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6598     else
6599       MovDir[x][y] = back_dir;
6600
6601     MovDelay[x][y] = 16 + 16 * RND(3);
6602   }
6603   else if (element == EL_DARK_YAMYAM)
6604   {
6605     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6606                                                          left_x, left_y);
6607     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6608                                                          right_x, right_y);
6609
6610     if (can_turn_left && can_turn_right)
6611       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6612     else if (can_turn_left)
6613       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6614     else if (can_turn_right)
6615       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6616     else
6617       MovDir[x][y] = back_dir;
6618
6619     MovDelay[x][y] = 16 + 16 * RND(3);
6620   }
6621   else if (element == EL_PACMAN)
6622   {
6623     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6624     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6625
6626     if (can_turn_left && can_turn_right)
6627       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6628     else if (can_turn_left)
6629       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6630     else if (can_turn_right)
6631       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6632     else
6633       MovDir[x][y] = back_dir;
6634
6635     MovDelay[x][y] = 6 + RND(40);
6636   }
6637   else if (element == EL_PIG)
6638   {
6639     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6640     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6641     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6642     boolean should_turn_left, should_turn_right, should_move_on;
6643     int rnd_value = 24;
6644     int rnd = RND(rnd_value);
6645
6646     should_turn_left = (can_turn_left &&
6647                         (!can_move_on ||
6648                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6649                                                    y + back_dy + left_dy)));
6650     should_turn_right = (can_turn_right &&
6651                          (!can_move_on ||
6652                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6653                                                     y + back_dy + right_dy)));
6654     should_move_on = (can_move_on &&
6655                       (!can_turn_left ||
6656                        !can_turn_right ||
6657                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6658                                                  y + move_dy + left_dy) ||
6659                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6660                                                  y + move_dy + right_dy)));
6661
6662     if (should_turn_left || should_turn_right || should_move_on)
6663     {
6664       if (should_turn_left && should_turn_right && should_move_on)
6665         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6666                         rnd < 2 * rnd_value / 3 ? right_dir :
6667                         old_move_dir);
6668       else if (should_turn_left && should_turn_right)
6669         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6670       else if (should_turn_left && should_move_on)
6671         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6672       else if (should_turn_right && should_move_on)
6673         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6674       else if (should_turn_left)
6675         MovDir[x][y] = left_dir;
6676       else if (should_turn_right)
6677         MovDir[x][y] = right_dir;
6678       else if (should_move_on)
6679         MovDir[x][y] = old_move_dir;
6680     }
6681     else if (can_move_on && rnd > rnd_value / 8)
6682       MovDir[x][y] = old_move_dir;
6683     else if (can_turn_left && can_turn_right)
6684       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6685     else if (can_turn_left && rnd > rnd_value / 8)
6686       MovDir[x][y] = left_dir;
6687     else if (can_turn_right && rnd > rnd_value/8)
6688       MovDir[x][y] = right_dir;
6689     else
6690       MovDir[x][y] = back_dir;
6691
6692     xx = x + move_xy[MovDir[x][y]].dx;
6693     yy = y + move_xy[MovDir[x][y]].dy;
6694
6695     if (!IN_LEV_FIELD(xx, yy) ||
6696         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6697       MovDir[x][y] = old_move_dir;
6698
6699     MovDelay[x][y] = 0;
6700   }
6701   else if (element == EL_DRAGON)
6702   {
6703     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6704     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6705     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6706     int rnd_value = 24;
6707     int rnd = RND(rnd_value);
6708
6709     if (can_move_on && rnd > rnd_value / 8)
6710       MovDir[x][y] = old_move_dir;
6711     else if (can_turn_left && can_turn_right)
6712       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6713     else if (can_turn_left && rnd > rnd_value / 8)
6714       MovDir[x][y] = left_dir;
6715     else if (can_turn_right && rnd > rnd_value / 8)
6716       MovDir[x][y] = right_dir;
6717     else
6718       MovDir[x][y] = back_dir;
6719
6720     xx = x + move_xy[MovDir[x][y]].dx;
6721     yy = y + move_xy[MovDir[x][y]].dy;
6722
6723     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6724       MovDir[x][y] = old_move_dir;
6725
6726     MovDelay[x][y] = 0;
6727   }
6728   else if (element == EL_MOLE)
6729   {
6730     boolean can_move_on =
6731       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6732                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6733                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6734     if (!can_move_on)
6735     {
6736       boolean can_turn_left =
6737         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6738                               IS_AMOEBOID(Feld[left_x][left_y])));
6739
6740       boolean can_turn_right =
6741         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6742                               IS_AMOEBOID(Feld[right_x][right_y])));
6743
6744       if (can_turn_left && can_turn_right)
6745         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6746       else if (can_turn_left)
6747         MovDir[x][y] = left_dir;
6748       else
6749         MovDir[x][y] = right_dir;
6750     }
6751
6752     if (MovDir[x][y] != old_move_dir)
6753       MovDelay[x][y] = 9;
6754   }
6755   else if (element == EL_BALLOON)
6756   {
6757     MovDir[x][y] = game.wind_direction;
6758     MovDelay[x][y] = 0;
6759   }
6760   else if (element == EL_SPRING)
6761   {
6762     if (MovDir[x][y] & MV_HORIZONTAL)
6763     {
6764       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6765           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6766       {
6767         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6768         ResetGfxAnimation(move_x, move_y);
6769         TEST_DrawLevelField(move_x, move_y);
6770
6771         MovDir[x][y] = back_dir;
6772       }
6773       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6774                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6775         MovDir[x][y] = MV_NONE;
6776     }
6777
6778     MovDelay[x][y] = 0;
6779   }
6780   else if (element == EL_ROBOT ||
6781            element == EL_SATELLITE ||
6782            element == EL_PENGUIN ||
6783            element == EL_EMC_ANDROID)
6784   {
6785     int attr_x = -1, attr_y = -1;
6786
6787     if (AllPlayersGone)
6788     {
6789       attr_x = ExitX;
6790       attr_y = ExitY;
6791     }
6792     else
6793     {
6794       int i;
6795
6796       for (i = 0; i < MAX_PLAYERS; i++)
6797       {
6798         struct PlayerInfo *player = &stored_player[i];
6799         int jx = player->jx, jy = player->jy;
6800
6801         if (!player->active)
6802           continue;
6803
6804         if (attr_x == -1 ||
6805             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6806         {
6807           attr_x = jx;
6808           attr_y = jy;
6809         }
6810       }
6811     }
6812
6813     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6814         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6815          game.engine_version < VERSION_IDENT(3,1,0,0)))
6816     {
6817       attr_x = ZX;
6818       attr_y = ZY;
6819     }
6820
6821     if (element == EL_PENGUIN)
6822     {
6823       int i;
6824       static int xy[4][2] =
6825       {
6826         { 0, -1 },
6827         { -1, 0 },
6828         { +1, 0 },
6829         { 0, +1 }
6830       };
6831
6832       for (i = 0; i < NUM_DIRECTIONS; i++)
6833       {
6834         int ex = x + xy[i][0];
6835         int ey = y + xy[i][1];
6836
6837         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6838                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6839                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6840                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6841         {
6842           attr_x = ex;
6843           attr_y = ey;
6844           break;
6845         }
6846       }
6847     }
6848
6849     MovDir[x][y] = MV_NONE;
6850     if (attr_x < x)
6851       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6852     else if (attr_x > x)
6853       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6854     if (attr_y < y)
6855       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6856     else if (attr_y > y)
6857       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6858
6859     if (element == EL_ROBOT)
6860     {
6861       int newx, newy;
6862
6863       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6864         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6865       Moving2Blocked(x, y, &newx, &newy);
6866
6867       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6868         MovDelay[x][y] = 8 + 8 * !RND(3);
6869       else
6870         MovDelay[x][y] = 16;
6871     }
6872     else if (element == EL_PENGUIN)
6873     {
6874       int newx, newy;
6875
6876       MovDelay[x][y] = 1;
6877
6878       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6879       {
6880         boolean first_horiz = RND(2);
6881         int new_move_dir = MovDir[x][y];
6882
6883         MovDir[x][y] =
6884           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6885         Moving2Blocked(x, y, &newx, &newy);
6886
6887         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6888           return;
6889
6890         MovDir[x][y] =
6891           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6892         Moving2Blocked(x, y, &newx, &newy);
6893
6894         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6895           return;
6896
6897         MovDir[x][y] = old_move_dir;
6898         return;
6899       }
6900     }
6901     else if (element == EL_SATELLITE)
6902     {
6903       int newx, newy;
6904
6905       MovDelay[x][y] = 1;
6906
6907       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6908       {
6909         boolean first_horiz = RND(2);
6910         int new_move_dir = MovDir[x][y];
6911
6912         MovDir[x][y] =
6913           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6914         Moving2Blocked(x, y, &newx, &newy);
6915
6916         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6917           return;
6918
6919         MovDir[x][y] =
6920           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6921         Moving2Blocked(x, y, &newx, &newy);
6922
6923         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6924           return;
6925
6926         MovDir[x][y] = old_move_dir;
6927         return;
6928       }
6929     }
6930     else if (element == EL_EMC_ANDROID)
6931     {
6932       static int check_pos[16] =
6933       {
6934         -1,             /*  0 => (invalid)          */
6935         7,              /*  1 => MV_LEFT            */
6936         3,              /*  2 => MV_RIGHT           */
6937         -1,             /*  3 => (invalid)          */
6938         1,              /*  4 =>            MV_UP   */
6939         0,              /*  5 => MV_LEFT  | MV_UP   */
6940         2,              /*  6 => MV_RIGHT | MV_UP   */
6941         -1,             /*  7 => (invalid)          */
6942         5,              /*  8 =>            MV_DOWN */
6943         6,              /*  9 => MV_LEFT  | MV_DOWN */
6944         4,              /* 10 => MV_RIGHT | MV_DOWN */
6945         -1,             /* 11 => (invalid)          */
6946         -1,             /* 12 => (invalid)          */
6947         -1,             /* 13 => (invalid)          */
6948         -1,             /* 14 => (invalid)          */
6949         -1,             /* 15 => (invalid)          */
6950       };
6951       static struct
6952       {
6953         int dx, dy;
6954         int dir;
6955       } check_xy[8] =
6956       {
6957         { -1, -1,       MV_LEFT  | MV_UP   },
6958         {  0, -1,                  MV_UP   },
6959         { +1, -1,       MV_RIGHT | MV_UP   },
6960         { +1,  0,       MV_RIGHT           },
6961         { +1, +1,       MV_RIGHT | MV_DOWN },
6962         {  0, +1,                  MV_DOWN },
6963         { -1, +1,       MV_LEFT  | MV_DOWN },
6964         { -1,  0,       MV_LEFT            },
6965       };
6966       int start_pos, check_order;
6967       boolean can_clone = FALSE;
6968       int i;
6969
6970       /* check if there is any free field around current position */
6971       for (i = 0; i < 8; i++)
6972       {
6973         int newx = x + check_xy[i].dx;
6974         int newy = y + check_xy[i].dy;
6975
6976         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6977         {
6978           can_clone = TRUE;
6979
6980           break;
6981         }
6982       }
6983
6984       if (can_clone)            /* randomly find an element to clone */
6985       {
6986         can_clone = FALSE;
6987
6988         start_pos = check_pos[RND(8)];
6989         check_order = (RND(2) ? -1 : +1);
6990
6991         for (i = 0; i < 8; i++)
6992         {
6993           int pos_raw = start_pos + i * check_order;
6994           int pos = (pos_raw + 8) % 8;
6995           int newx = x + check_xy[pos].dx;
6996           int newy = y + check_xy[pos].dy;
6997
6998           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6999           {
7000             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7001             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7002
7003             Store[x][y] = Feld[newx][newy];
7004
7005             can_clone = TRUE;
7006
7007             break;
7008           }
7009         }
7010       }
7011
7012       if (can_clone)            /* randomly find a direction to move */
7013       {
7014         can_clone = FALSE;
7015
7016         start_pos = check_pos[RND(8)];
7017         check_order = (RND(2) ? -1 : +1);
7018
7019         for (i = 0; i < 8; i++)
7020         {
7021           int pos_raw = start_pos + i * check_order;
7022           int pos = (pos_raw + 8) % 8;
7023           int newx = x + check_xy[pos].dx;
7024           int newy = y + check_xy[pos].dy;
7025           int new_move_dir = check_xy[pos].dir;
7026
7027           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7028           {
7029             MovDir[x][y] = new_move_dir;
7030             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7031
7032             can_clone = TRUE;
7033
7034             break;
7035           }
7036         }
7037       }
7038
7039       if (can_clone)            /* cloning and moving successful */
7040         return;
7041
7042       /* cannot clone -- try to move towards player */
7043
7044       start_pos = check_pos[MovDir[x][y] & 0x0f];
7045       check_order = (RND(2) ? -1 : +1);
7046
7047       for (i = 0; i < 3; i++)
7048       {
7049         /* first check start_pos, then previous/next or (next/previous) pos */
7050         int pos_raw = start_pos + (i < 2 ? i : -1) * 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         int new_move_dir = check_xy[pos].dir;
7055
7056         if (IS_PLAYER(newx, newy))
7057           break;
7058
7059         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7060         {
7061           MovDir[x][y] = new_move_dir;
7062           MovDelay[x][y] = level.android_move_time * 8 + 1;
7063
7064           break;
7065         }
7066       }
7067     }
7068   }
7069   else if (move_pattern == MV_TURNING_LEFT ||
7070            move_pattern == MV_TURNING_RIGHT ||
7071            move_pattern == MV_TURNING_LEFT_RIGHT ||
7072            move_pattern == MV_TURNING_RIGHT_LEFT ||
7073            move_pattern == MV_TURNING_RANDOM ||
7074            move_pattern == MV_ALL_DIRECTIONS)
7075   {
7076     boolean can_turn_left =
7077       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7078     boolean can_turn_right =
7079       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7080
7081     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7082       return;
7083
7084     if (move_pattern == MV_TURNING_LEFT)
7085       MovDir[x][y] = left_dir;
7086     else if (move_pattern == MV_TURNING_RIGHT)
7087       MovDir[x][y] = right_dir;
7088     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7089       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7090     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7091       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7092     else if (move_pattern == MV_TURNING_RANDOM)
7093       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7094                       can_turn_right && !can_turn_left ? right_dir :
7095                       RND(2) ? left_dir : right_dir);
7096     else if (can_turn_left && can_turn_right)
7097       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7098     else if (can_turn_left)
7099       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7100     else if (can_turn_right)
7101       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7102     else
7103       MovDir[x][y] = back_dir;
7104
7105     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7106   }
7107   else if (move_pattern == MV_HORIZONTAL ||
7108            move_pattern == MV_VERTICAL)
7109   {
7110     if (move_pattern & old_move_dir)
7111       MovDir[x][y] = back_dir;
7112     else if (move_pattern == MV_HORIZONTAL)
7113       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7114     else if (move_pattern == MV_VERTICAL)
7115       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7116
7117     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118   }
7119   else if (move_pattern & MV_ANY_DIRECTION)
7120   {
7121     MovDir[x][y] = move_pattern;
7122     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7123   }
7124   else if (move_pattern & MV_WIND_DIRECTION)
7125   {
7126     MovDir[x][y] = game.wind_direction;
7127     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128   }
7129   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7130   {
7131     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7132       MovDir[x][y] = left_dir;
7133     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7134       MovDir[x][y] = right_dir;
7135
7136     if (MovDir[x][y] != old_move_dir)
7137       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7138   }
7139   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7140   {
7141     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7142       MovDir[x][y] = right_dir;
7143     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7144       MovDir[x][y] = left_dir;
7145
7146     if (MovDir[x][y] != old_move_dir)
7147       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7148   }
7149   else if (move_pattern == MV_TOWARDS_PLAYER ||
7150            move_pattern == MV_AWAY_FROM_PLAYER)
7151   {
7152     int attr_x = -1, attr_y = -1;
7153     int newx, newy;
7154     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7155
7156     if (AllPlayersGone)
7157     {
7158       attr_x = ExitX;
7159       attr_y = ExitY;
7160     }
7161     else
7162     {
7163       int i;
7164
7165       for (i = 0; i < MAX_PLAYERS; i++)
7166       {
7167         struct PlayerInfo *player = &stored_player[i];
7168         int jx = player->jx, jy = player->jy;
7169
7170         if (!player->active)
7171           continue;
7172
7173         if (attr_x == -1 ||
7174             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7175         {
7176           attr_x = jx;
7177           attr_y = jy;
7178         }
7179       }
7180     }
7181
7182     MovDir[x][y] = MV_NONE;
7183     if (attr_x < x)
7184       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7185     else if (attr_x > x)
7186       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7187     if (attr_y < y)
7188       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7189     else if (attr_y > y)
7190       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7191
7192     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7193
7194     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7195     {
7196       boolean first_horiz = RND(2);
7197       int new_move_dir = MovDir[x][y];
7198
7199       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7200       {
7201         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7202         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7203
7204         return;
7205       }
7206
7207       MovDir[x][y] =
7208         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7209       Moving2Blocked(x, y, &newx, &newy);
7210
7211       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7212         return;
7213
7214       MovDir[x][y] =
7215         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7216       Moving2Blocked(x, y, &newx, &newy);
7217
7218       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7219         return;
7220
7221       MovDir[x][y] = old_move_dir;
7222     }
7223   }
7224   else if (move_pattern == MV_WHEN_PUSHED ||
7225            move_pattern == MV_WHEN_DROPPED)
7226   {
7227     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7228       MovDir[x][y] = MV_NONE;
7229
7230     MovDelay[x][y] = 0;
7231   }
7232   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7233   {
7234     static int test_xy[7][2] =
7235     {
7236       { 0, -1 },
7237       { -1, 0 },
7238       { +1, 0 },
7239       { 0, +1 },
7240       { 0, -1 },
7241       { -1, 0 },
7242       { +1, 0 },
7243     };
7244     static int test_dir[7] =
7245     {
7246       MV_UP,
7247       MV_LEFT,
7248       MV_RIGHT,
7249       MV_DOWN,
7250       MV_UP,
7251       MV_LEFT,
7252       MV_RIGHT,
7253     };
7254     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7255     int move_preference = -1000000;     /* start with very low preference */
7256     int new_move_dir = MV_NONE;
7257     int start_test = RND(4);
7258     int i;
7259
7260     for (i = 0; i < NUM_DIRECTIONS; i++)
7261     {
7262       int move_dir = test_dir[start_test + i];
7263       int move_dir_preference;
7264
7265       xx = x + test_xy[start_test + i][0];
7266       yy = y + test_xy[start_test + i][1];
7267
7268       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7269           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7270       {
7271         new_move_dir = move_dir;
7272
7273         break;
7274       }
7275
7276       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7277         continue;
7278
7279       move_dir_preference = -1 * RunnerVisit[xx][yy];
7280       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7281         move_dir_preference = PlayerVisit[xx][yy];
7282
7283       if (move_dir_preference > move_preference)
7284       {
7285         /* prefer field that has not been visited for the longest time */
7286         move_preference = move_dir_preference;
7287         new_move_dir = move_dir;
7288       }
7289       else if (move_dir_preference == move_preference &&
7290                move_dir == old_move_dir)
7291       {
7292         /* prefer last direction when all directions are preferred equally */
7293         move_preference = move_dir_preference;
7294         new_move_dir = move_dir;
7295       }
7296     }
7297
7298     MovDir[x][y] = new_move_dir;
7299     if (old_move_dir != new_move_dir)
7300       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7301   }
7302 }
7303
7304 static void TurnRound(int x, int y)
7305 {
7306   int direction = MovDir[x][y];
7307
7308   TurnRoundExt(x, y);
7309
7310   GfxDir[x][y] = MovDir[x][y];
7311
7312   if (direction != MovDir[x][y])
7313     GfxFrame[x][y] = 0;
7314
7315   if (MovDelay[x][y])
7316     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7317
7318   ResetGfxFrame(x, y);
7319 }
7320
7321 static boolean JustBeingPushed(int x, int y)
7322 {
7323   int i;
7324
7325   for (i = 0; i < MAX_PLAYERS; i++)
7326   {
7327     struct PlayerInfo *player = &stored_player[i];
7328
7329     if (player->active && player->is_pushing && player->MovPos)
7330     {
7331       int next_jx = player->jx + (player->jx - player->last_jx);
7332       int next_jy = player->jy + (player->jy - player->last_jy);
7333
7334       if (x == next_jx && y == next_jy)
7335         return TRUE;
7336     }
7337   }
7338
7339   return FALSE;
7340 }
7341
7342 static void StartMoving(int x, int y)
7343 {
7344   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7345   int element = Feld[x][y];
7346
7347   if (Stop[x][y])
7348     return;
7349
7350   if (MovDelay[x][y] == 0)
7351     GfxAction[x][y] = ACTION_DEFAULT;
7352
7353   if (CAN_FALL(element) && y < lev_fieldy - 1)
7354   {
7355     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7356         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7357       if (JustBeingPushed(x, y))
7358         return;
7359
7360     if (element == EL_QUICKSAND_FULL)
7361     {
7362       if (IS_FREE(x, y + 1))
7363       {
7364         InitMovingField(x, y, MV_DOWN);
7365         started_moving = TRUE;
7366
7367         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7368 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7369         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7370           Store[x][y] = EL_ROCK;
7371 #else
7372         Store[x][y] = EL_ROCK;
7373 #endif
7374
7375         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7376       }
7377       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7378       {
7379         if (!MovDelay[x][y])
7380         {
7381           MovDelay[x][y] = TILEY + 1;
7382
7383           ResetGfxAnimation(x, y);
7384           ResetGfxAnimation(x, y + 1);
7385         }
7386
7387         if (MovDelay[x][y])
7388         {
7389           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7390           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7391
7392           MovDelay[x][y]--;
7393           if (MovDelay[x][y])
7394             return;
7395         }
7396
7397         Feld[x][y] = EL_QUICKSAND_EMPTY;
7398         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7399         Store[x][y + 1] = Store[x][y];
7400         Store[x][y] = 0;
7401
7402         PlayLevelSoundAction(x, y, ACTION_FILLING);
7403       }
7404       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7405       {
7406         if (!MovDelay[x][y])
7407         {
7408           MovDelay[x][y] = TILEY + 1;
7409
7410           ResetGfxAnimation(x, y);
7411           ResetGfxAnimation(x, y + 1);
7412         }
7413
7414         if (MovDelay[x][y])
7415         {
7416           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7417           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7418
7419           MovDelay[x][y]--;
7420           if (MovDelay[x][y])
7421             return;
7422         }
7423
7424         Feld[x][y] = EL_QUICKSAND_EMPTY;
7425         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7426         Store[x][y + 1] = Store[x][y];
7427         Store[x][y] = 0;
7428
7429         PlayLevelSoundAction(x, y, ACTION_FILLING);
7430       }
7431     }
7432     else if (element == EL_QUICKSAND_FAST_FULL)
7433     {
7434       if (IS_FREE(x, y + 1))
7435       {
7436         InitMovingField(x, y, MV_DOWN);
7437         started_moving = TRUE;
7438
7439         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7440 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7441         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7442           Store[x][y] = EL_ROCK;
7443 #else
7444         Store[x][y] = EL_ROCK;
7445 #endif
7446
7447         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7448       }
7449       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7450       {
7451         if (!MovDelay[x][y])
7452         {
7453           MovDelay[x][y] = TILEY + 1;
7454
7455           ResetGfxAnimation(x, y);
7456           ResetGfxAnimation(x, y + 1);
7457         }
7458
7459         if (MovDelay[x][y])
7460         {
7461           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7462           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7463
7464           MovDelay[x][y]--;
7465           if (MovDelay[x][y])
7466             return;
7467         }
7468
7469         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7470         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7471         Store[x][y + 1] = Store[x][y];
7472         Store[x][y] = 0;
7473
7474         PlayLevelSoundAction(x, y, ACTION_FILLING);
7475       }
7476       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7477       {
7478         if (!MovDelay[x][y])
7479         {
7480           MovDelay[x][y] = TILEY + 1;
7481
7482           ResetGfxAnimation(x, y);
7483           ResetGfxAnimation(x, y + 1);
7484         }
7485
7486         if (MovDelay[x][y])
7487         {
7488           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7489           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7490
7491           MovDelay[x][y]--;
7492           if (MovDelay[x][y])
7493             return;
7494         }
7495
7496         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7497         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7498         Store[x][y + 1] = Store[x][y];
7499         Store[x][y] = 0;
7500
7501         PlayLevelSoundAction(x, y, ACTION_FILLING);
7502       }
7503     }
7504     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7505              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7506     {
7507       InitMovingField(x, y, MV_DOWN);
7508       started_moving = TRUE;
7509
7510       Feld[x][y] = EL_QUICKSAND_FILLING;
7511       Store[x][y] = element;
7512
7513       PlayLevelSoundAction(x, y, ACTION_FILLING);
7514     }
7515     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7516              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7517     {
7518       InitMovingField(x, y, MV_DOWN);
7519       started_moving = TRUE;
7520
7521       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7522       Store[x][y] = element;
7523
7524       PlayLevelSoundAction(x, y, ACTION_FILLING);
7525     }
7526     else if (element == EL_MAGIC_WALL_FULL)
7527     {
7528       if (IS_FREE(x, y + 1))
7529       {
7530         InitMovingField(x, y, MV_DOWN);
7531         started_moving = TRUE;
7532
7533         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7534         Store[x][y] = EL_CHANGED(Store[x][y]);
7535       }
7536       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7537       {
7538         if (!MovDelay[x][y])
7539           MovDelay[x][y] = TILEY / 4 + 1;
7540
7541         if (MovDelay[x][y])
7542         {
7543           MovDelay[x][y]--;
7544           if (MovDelay[x][y])
7545             return;
7546         }
7547
7548         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7549         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7550         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7551         Store[x][y] = 0;
7552       }
7553     }
7554     else if (element == EL_BD_MAGIC_WALL_FULL)
7555     {
7556       if (IS_FREE(x, y + 1))
7557       {
7558         InitMovingField(x, y, MV_DOWN);
7559         started_moving = TRUE;
7560
7561         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7562         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7563       }
7564       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7565       {
7566         if (!MovDelay[x][y])
7567           MovDelay[x][y] = TILEY / 4 + 1;
7568
7569         if (MovDelay[x][y])
7570         {
7571           MovDelay[x][y]--;
7572           if (MovDelay[x][y])
7573             return;
7574         }
7575
7576         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7577         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7578         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7579         Store[x][y] = 0;
7580       }
7581     }
7582     else if (element == EL_DC_MAGIC_WALL_FULL)
7583     {
7584       if (IS_FREE(x, y + 1))
7585       {
7586         InitMovingField(x, y, MV_DOWN);
7587         started_moving = TRUE;
7588
7589         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7590         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7591       }
7592       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7593       {
7594         if (!MovDelay[x][y])
7595           MovDelay[x][y] = TILEY / 4 + 1;
7596
7597         if (MovDelay[x][y])
7598         {
7599           MovDelay[x][y]--;
7600           if (MovDelay[x][y])
7601             return;
7602         }
7603
7604         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7605         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7606         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7607         Store[x][y] = 0;
7608       }
7609     }
7610     else if ((CAN_PASS_MAGIC_WALL(element) &&
7611               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7612                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7613              (CAN_PASS_DC_MAGIC_WALL(element) &&
7614               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7615
7616     {
7617       InitMovingField(x, y, MV_DOWN);
7618       started_moving = TRUE;
7619
7620       Feld[x][y] =
7621         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7622          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7623          EL_DC_MAGIC_WALL_FILLING);
7624       Store[x][y] = element;
7625     }
7626     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7627     {
7628       SplashAcid(x, y + 1);
7629
7630       InitMovingField(x, y, MV_DOWN);
7631       started_moving = TRUE;
7632
7633       Store[x][y] = EL_ACID;
7634     }
7635     else if (
7636              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7637               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7638              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7639               CAN_FALL(element) && WasJustFalling[x][y] &&
7640               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7641
7642              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7643               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7644               (Feld[x][y + 1] == EL_BLOCKED)))
7645     {
7646       /* this is needed for a special case not covered by calling "Impact()"
7647          from "ContinueMoving()": if an element moves to a tile directly below
7648          another element which was just falling on that tile (which was empty
7649          in the previous frame), the falling element above would just stop
7650          instead of smashing the element below (in previous version, the above
7651          element was just checked for "moving" instead of "falling", resulting
7652          in incorrect smashes caused by horizontal movement of the above
7653          element; also, the case of the player being the element to smash was
7654          simply not covered here... :-/ ) */
7655
7656       CheckCollision[x][y] = 0;
7657       CheckImpact[x][y] = 0;
7658
7659       Impact(x, y);
7660     }
7661     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7662     {
7663       if (MovDir[x][y] == MV_NONE)
7664       {
7665         InitMovingField(x, y, MV_DOWN);
7666         started_moving = TRUE;
7667       }
7668     }
7669     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7670     {
7671       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7672         MovDir[x][y] = MV_DOWN;
7673
7674       InitMovingField(x, y, MV_DOWN);
7675       started_moving = TRUE;
7676     }
7677     else if (element == EL_AMOEBA_DROP)
7678     {
7679       Feld[x][y] = EL_AMOEBA_GROWING;
7680       Store[x][y] = EL_AMOEBA_WET;
7681     }
7682     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7683               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7684              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7685              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7686     {
7687       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7688                                 (IS_FREE(x - 1, y + 1) ||
7689                                  Feld[x - 1][y + 1] == EL_ACID));
7690       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7691                                 (IS_FREE(x + 1, y + 1) ||
7692                                  Feld[x + 1][y + 1] == EL_ACID));
7693       boolean can_fall_any  = (can_fall_left || can_fall_right);
7694       boolean can_fall_both = (can_fall_left && can_fall_right);
7695       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7696
7697       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7698       {
7699         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7700           can_fall_right = FALSE;
7701         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7702           can_fall_left = FALSE;
7703         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7704           can_fall_right = FALSE;
7705         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7706           can_fall_left = FALSE;
7707
7708         can_fall_any  = (can_fall_left || can_fall_right);
7709         can_fall_both = FALSE;
7710       }
7711
7712       if (can_fall_both)
7713       {
7714         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7715           can_fall_right = FALSE;       /* slip down on left side */
7716         else
7717           can_fall_left = !(can_fall_right = RND(2));
7718
7719         can_fall_both = FALSE;
7720       }
7721
7722       if (can_fall_any)
7723       {
7724         /* if not determined otherwise, prefer left side for slipping down */
7725         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7726         started_moving = TRUE;
7727       }
7728     }
7729     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7730     {
7731       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7732       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7733       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7734       int belt_dir = game.belt_dir[belt_nr];
7735
7736       if ((belt_dir == MV_LEFT  && left_is_free) ||
7737           (belt_dir == MV_RIGHT && right_is_free))
7738       {
7739         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7740
7741         InitMovingField(x, y, belt_dir);
7742         started_moving = TRUE;
7743
7744         Pushed[x][y] = TRUE;
7745         Pushed[nextx][y] = TRUE;
7746
7747         GfxAction[x][y] = ACTION_DEFAULT;
7748       }
7749       else
7750       {
7751         MovDir[x][y] = 0;       /* if element was moving, stop it */
7752       }
7753     }
7754   }
7755
7756   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7757   if (CAN_MOVE(element) && !started_moving)
7758   {
7759     int move_pattern = element_info[element].move_pattern;
7760     int newx, newy;
7761
7762     Moving2Blocked(x, y, &newx, &newy);
7763
7764     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7765       return;
7766
7767     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7768         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7769     {
7770       WasJustMoving[x][y] = 0;
7771       CheckCollision[x][y] = 0;
7772
7773       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7774
7775       if (Feld[x][y] != element)        /* element has changed */
7776         return;
7777     }
7778
7779     if (!MovDelay[x][y])        /* start new movement phase */
7780     {
7781       /* all objects that can change their move direction after each step
7782          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7783
7784       if (element != EL_YAMYAM &&
7785           element != EL_DARK_YAMYAM &&
7786           element != EL_PACMAN &&
7787           !(move_pattern & MV_ANY_DIRECTION) &&
7788           move_pattern != MV_TURNING_LEFT &&
7789           move_pattern != MV_TURNING_RIGHT &&
7790           move_pattern != MV_TURNING_LEFT_RIGHT &&
7791           move_pattern != MV_TURNING_RIGHT_LEFT &&
7792           move_pattern != MV_TURNING_RANDOM)
7793       {
7794         TurnRound(x, y);
7795
7796         if (MovDelay[x][y] && (element == EL_BUG ||
7797                                element == EL_SPACESHIP ||
7798                                element == EL_SP_SNIKSNAK ||
7799                                element == EL_SP_ELECTRON ||
7800                                element == EL_MOLE))
7801           TEST_DrawLevelField(x, y);
7802       }
7803     }
7804
7805     if (MovDelay[x][y])         /* wait some time before next movement */
7806     {
7807       MovDelay[x][y]--;
7808
7809       if (element == EL_ROBOT ||
7810           element == EL_YAMYAM ||
7811           element == EL_DARK_YAMYAM)
7812       {
7813         DrawLevelElementAnimationIfNeeded(x, y, element);
7814         PlayLevelSoundAction(x, y, ACTION_WAITING);
7815       }
7816       else if (element == EL_SP_ELECTRON)
7817         DrawLevelElementAnimationIfNeeded(x, y, element);
7818       else if (element == EL_DRAGON)
7819       {
7820         int i;
7821         int dir = MovDir[x][y];
7822         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7823         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7824         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7825                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7826                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7827                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7828         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7829
7830         GfxAction[x][y] = ACTION_ATTACKING;
7831
7832         if (IS_PLAYER(x, y))
7833           DrawPlayerField(x, y);
7834         else
7835           TEST_DrawLevelField(x, y);
7836
7837         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7838
7839         for (i = 1; i <= 3; i++)
7840         {
7841           int xx = x + i * dx;
7842           int yy = y + i * dy;
7843           int sx = SCREENX(xx);
7844           int sy = SCREENY(yy);
7845           int flame_graphic = graphic + (i - 1);
7846
7847           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7848             break;
7849
7850           if (MovDelay[x][y])
7851           {
7852             int flamed = MovingOrBlocked2Element(xx, yy);
7853
7854             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7855               Bang(xx, yy);
7856             else
7857               RemoveMovingField(xx, yy);
7858
7859             ChangeDelay[xx][yy] = 0;
7860
7861             Feld[xx][yy] = EL_FLAMES;
7862
7863             if (IN_SCR_FIELD(sx, sy))
7864             {
7865               TEST_DrawLevelFieldCrumbled(xx, yy);
7866               DrawGraphic(sx, sy, flame_graphic, frame);
7867             }
7868           }
7869           else
7870           {
7871             if (Feld[xx][yy] == EL_FLAMES)
7872               Feld[xx][yy] = EL_EMPTY;
7873             TEST_DrawLevelField(xx, yy);
7874           }
7875         }
7876       }
7877
7878       if (MovDelay[x][y])       /* element still has to wait some time */
7879       {
7880         PlayLevelSoundAction(x, y, ACTION_WAITING);
7881
7882         return;
7883       }
7884     }
7885
7886     /* now make next step */
7887
7888     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7889
7890     if (DONT_COLLIDE_WITH(element) &&
7891         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7892         !PLAYER_ENEMY_PROTECTED(newx, newy))
7893     {
7894       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7895
7896       return;
7897     }
7898
7899     else if (CAN_MOVE_INTO_ACID(element) &&
7900              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7901              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7902              (MovDir[x][y] == MV_DOWN ||
7903               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7904     {
7905       SplashAcid(newx, newy);
7906       Store[x][y] = EL_ACID;
7907     }
7908     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7909     {
7910       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7911           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7912           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7913           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7914       {
7915         RemoveField(x, y);
7916         TEST_DrawLevelField(x, y);
7917
7918         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7919         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7920           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7921
7922         local_player->friends_still_needed--;
7923         if (!local_player->friends_still_needed &&
7924             !local_player->GameOver && AllPlayersGone)
7925           PlayerWins(local_player);
7926
7927         return;
7928       }
7929       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7930       {
7931         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7932           TEST_DrawLevelField(newx, newy);
7933         else
7934           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7935       }
7936       else if (!IS_FREE(newx, newy))
7937       {
7938         GfxAction[x][y] = ACTION_WAITING;
7939
7940         if (IS_PLAYER(x, y))
7941           DrawPlayerField(x, y);
7942         else
7943           TEST_DrawLevelField(x, y);
7944
7945         return;
7946       }
7947     }
7948     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7949     {
7950       if (IS_FOOD_PIG(Feld[newx][newy]))
7951       {
7952         if (IS_MOVING(newx, newy))
7953           RemoveMovingField(newx, newy);
7954         else
7955         {
7956           Feld[newx][newy] = EL_EMPTY;
7957           TEST_DrawLevelField(newx, newy);
7958         }
7959
7960         PlayLevelSound(x, y, SND_PIG_DIGGING);
7961       }
7962       else if (!IS_FREE(newx, newy))
7963       {
7964         if (IS_PLAYER(x, y))
7965           DrawPlayerField(x, y);
7966         else
7967           TEST_DrawLevelField(x, y);
7968
7969         return;
7970       }
7971     }
7972     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7973     {
7974       if (Store[x][y] != EL_EMPTY)
7975       {
7976         boolean can_clone = FALSE;
7977         int xx, yy;
7978
7979         /* check if element to clone is still there */
7980         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7981         {
7982           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7983           {
7984             can_clone = TRUE;
7985
7986             break;
7987           }
7988         }
7989
7990         /* cannot clone or target field not free anymore -- do not clone */
7991         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7992           Store[x][y] = EL_EMPTY;
7993       }
7994
7995       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7996       {
7997         if (IS_MV_DIAGONAL(MovDir[x][y]))
7998         {
7999           int diagonal_move_dir = MovDir[x][y];
8000           int stored = Store[x][y];
8001           int change_delay = 8;
8002           int graphic;
8003
8004           /* android is moving diagonally */
8005
8006           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8007
8008           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8009           GfxElement[x][y] = EL_EMC_ANDROID;
8010           GfxAction[x][y] = ACTION_SHRINKING;
8011           GfxDir[x][y] = diagonal_move_dir;
8012           ChangeDelay[x][y] = change_delay;
8013
8014           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8015                                    GfxDir[x][y]);
8016
8017           DrawLevelGraphicAnimation(x, y, graphic);
8018           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8019
8020           if (Feld[newx][newy] == EL_ACID)
8021           {
8022             SplashAcid(newx, newy);
8023
8024             return;
8025           }
8026
8027           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8028
8029           Store[newx][newy] = EL_EMC_ANDROID;
8030           GfxElement[newx][newy] = EL_EMC_ANDROID;
8031           GfxAction[newx][newy] = ACTION_GROWING;
8032           GfxDir[newx][newy] = diagonal_move_dir;
8033           ChangeDelay[newx][newy] = change_delay;
8034
8035           graphic = el_act_dir2img(GfxElement[newx][newy],
8036                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8037
8038           DrawLevelGraphicAnimation(newx, newy, graphic);
8039           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8040
8041           return;
8042         }
8043         else
8044         {
8045           Feld[newx][newy] = EL_EMPTY;
8046           TEST_DrawLevelField(newx, newy);
8047
8048           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8049         }
8050       }
8051       else if (!IS_FREE(newx, newy))
8052       {
8053         return;
8054       }
8055     }
8056     else if (IS_CUSTOM_ELEMENT(element) &&
8057              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8058     {
8059       if (!DigFieldByCE(newx, newy, element))
8060         return;
8061
8062       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8063       {
8064         RunnerVisit[x][y] = FrameCounter;
8065         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8066       }
8067     }
8068     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8069     {
8070       if (!IS_FREE(newx, newy))
8071       {
8072         if (IS_PLAYER(x, y))
8073           DrawPlayerField(x, y);
8074         else
8075           TEST_DrawLevelField(x, y);
8076
8077         return;
8078       }
8079       else
8080       {
8081         boolean wanna_flame = !RND(10);
8082         int dx = newx - x, dy = newy - y;
8083         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8084         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8085         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8086                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8087         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8088                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8089
8090         if ((wanna_flame ||
8091              IS_CLASSIC_ENEMY(element1) ||
8092              IS_CLASSIC_ENEMY(element2)) &&
8093             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8094             element1 != EL_FLAMES && element2 != EL_FLAMES)
8095         {
8096           ResetGfxAnimation(x, y);
8097           GfxAction[x][y] = ACTION_ATTACKING;
8098
8099           if (IS_PLAYER(x, y))
8100             DrawPlayerField(x, y);
8101           else
8102             TEST_DrawLevelField(x, y);
8103
8104           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8105
8106           MovDelay[x][y] = 50;
8107
8108           Feld[newx][newy] = EL_FLAMES;
8109           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8110             Feld[newx1][newy1] = EL_FLAMES;
8111           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8112             Feld[newx2][newy2] = EL_FLAMES;
8113
8114           return;
8115         }
8116       }
8117     }
8118     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8119              Feld[newx][newy] == EL_DIAMOND)
8120     {
8121       if (IS_MOVING(newx, newy))
8122         RemoveMovingField(newx, newy);
8123       else
8124       {
8125         Feld[newx][newy] = EL_EMPTY;
8126         TEST_DrawLevelField(newx, newy);
8127       }
8128
8129       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8130     }
8131     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8132              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8133     {
8134       if (AmoebaNr[newx][newy])
8135       {
8136         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8137         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8138             Feld[newx][newy] == EL_BD_AMOEBA)
8139           AmoebaCnt[AmoebaNr[newx][newy]]--;
8140       }
8141
8142       if (IS_MOVING(newx, newy))
8143       {
8144         RemoveMovingField(newx, newy);
8145       }
8146       else
8147       {
8148         Feld[newx][newy] = EL_EMPTY;
8149         TEST_DrawLevelField(newx, newy);
8150       }
8151
8152       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8153     }
8154     else if ((element == EL_PACMAN || element == EL_MOLE)
8155              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8156     {
8157       if (AmoebaNr[newx][newy])
8158       {
8159         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8160         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8161             Feld[newx][newy] == EL_BD_AMOEBA)
8162           AmoebaCnt[AmoebaNr[newx][newy]]--;
8163       }
8164
8165       if (element == EL_MOLE)
8166       {
8167         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8168         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8169
8170         ResetGfxAnimation(x, y);
8171         GfxAction[x][y] = ACTION_DIGGING;
8172         TEST_DrawLevelField(x, y);
8173
8174         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8175
8176         return;                         /* wait for shrinking amoeba */
8177       }
8178       else      /* element == EL_PACMAN */
8179       {
8180         Feld[newx][newy] = EL_EMPTY;
8181         TEST_DrawLevelField(newx, newy);
8182         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8183       }
8184     }
8185     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8186              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8187               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8188     {
8189       /* wait for shrinking amoeba to completely disappear */
8190       return;
8191     }
8192     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8193     {
8194       /* object was running against a wall */
8195
8196       TurnRound(x, y);
8197
8198       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8199         DrawLevelElementAnimation(x, y, element);
8200
8201       if (DONT_TOUCH(element))
8202         TestIfBadThingTouchesPlayer(x, y);
8203
8204       return;
8205     }
8206
8207     InitMovingField(x, y, MovDir[x][y]);
8208
8209     PlayLevelSoundAction(x, y, ACTION_MOVING);
8210   }
8211
8212   if (MovDir[x][y])
8213     ContinueMoving(x, y);
8214 }
8215
8216 void ContinueMoving(int x, int y)
8217 {
8218   int element = Feld[x][y];
8219   struct ElementInfo *ei = &element_info[element];
8220   int direction = MovDir[x][y];
8221   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8222   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8223   int newx = x + dx, newy = y + dy;
8224   int stored = Store[x][y];
8225   int stored_new = Store[newx][newy];
8226   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8227   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8228   boolean last_line = (newy == lev_fieldy - 1);
8229
8230   MovPos[x][y] += getElementMoveStepsize(x, y);
8231
8232   if (pushed_by_player) /* special case: moving object pushed by player */
8233     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8234
8235   if (ABS(MovPos[x][y]) < TILEX)
8236   {
8237     TEST_DrawLevelField(x, y);
8238
8239     return;     /* element is still moving */
8240   }
8241
8242   /* element reached destination field */
8243
8244   Feld[x][y] = EL_EMPTY;
8245   Feld[newx][newy] = element;
8246   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8247
8248   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8249   {
8250     element = Feld[newx][newy] = EL_ACID;
8251   }
8252   else if (element == EL_MOLE)
8253   {
8254     Feld[x][y] = EL_SAND;
8255
8256     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8257   }
8258   else if (element == EL_QUICKSAND_FILLING)
8259   {
8260     element = Feld[newx][newy] = get_next_element(element);
8261     Store[newx][newy] = Store[x][y];
8262   }
8263   else if (element == EL_QUICKSAND_EMPTYING)
8264   {
8265     Feld[x][y] = get_next_element(element);
8266     element = Feld[newx][newy] = Store[x][y];
8267   }
8268   else if (element == EL_QUICKSAND_FAST_FILLING)
8269   {
8270     element = Feld[newx][newy] = get_next_element(element);
8271     Store[newx][newy] = Store[x][y];
8272   }
8273   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8274   {
8275     Feld[x][y] = get_next_element(element);
8276     element = Feld[newx][newy] = Store[x][y];
8277   }
8278   else if (element == EL_MAGIC_WALL_FILLING)
8279   {
8280     element = Feld[newx][newy] = get_next_element(element);
8281     if (!game.magic_wall_active)
8282       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8283     Store[newx][newy] = Store[x][y];
8284   }
8285   else if (element == EL_MAGIC_WALL_EMPTYING)
8286   {
8287     Feld[x][y] = get_next_element(element);
8288     if (!game.magic_wall_active)
8289       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8290     element = Feld[newx][newy] = Store[x][y];
8291
8292     InitField(newx, newy, FALSE);
8293   }
8294   else if (element == EL_BD_MAGIC_WALL_FILLING)
8295   {
8296     element = Feld[newx][newy] = get_next_element(element);
8297     if (!game.magic_wall_active)
8298       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8299     Store[newx][newy] = Store[x][y];
8300   }
8301   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8302   {
8303     Feld[x][y] = get_next_element(element);
8304     if (!game.magic_wall_active)
8305       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8306     element = Feld[newx][newy] = Store[x][y];
8307
8308     InitField(newx, newy, FALSE);
8309   }
8310   else if (element == EL_DC_MAGIC_WALL_FILLING)
8311   {
8312     element = Feld[newx][newy] = get_next_element(element);
8313     if (!game.magic_wall_active)
8314       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8315     Store[newx][newy] = Store[x][y];
8316   }
8317   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8318   {
8319     Feld[x][y] = get_next_element(element);
8320     if (!game.magic_wall_active)
8321       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8322     element = Feld[newx][newy] = Store[x][y];
8323
8324     InitField(newx, newy, FALSE);
8325   }
8326   else if (element == EL_AMOEBA_DROPPING)
8327   {
8328     Feld[x][y] = get_next_element(element);
8329     element = Feld[newx][newy] = Store[x][y];
8330   }
8331   else if (element == EL_SOKOBAN_OBJECT)
8332   {
8333     if (Back[x][y])
8334       Feld[x][y] = Back[x][y];
8335
8336     if (Back[newx][newy])
8337       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8338
8339     Back[x][y] = Back[newx][newy] = 0;
8340   }
8341
8342   Store[x][y] = EL_EMPTY;
8343   MovPos[x][y] = 0;
8344   MovDir[x][y] = 0;
8345   MovDelay[x][y] = 0;
8346
8347   MovDelay[newx][newy] = 0;
8348
8349   if (CAN_CHANGE_OR_HAS_ACTION(element))
8350   {
8351     /* copy element change control values to new field */
8352     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8353     ChangePage[newx][newy]  = ChangePage[x][y];
8354     ChangeCount[newx][newy] = ChangeCount[x][y];
8355     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8356   }
8357
8358   CustomValue[newx][newy] = CustomValue[x][y];
8359
8360   ChangeDelay[x][y] = 0;
8361   ChangePage[x][y] = -1;
8362   ChangeCount[x][y] = 0;
8363   ChangeEvent[x][y] = -1;
8364
8365   CustomValue[x][y] = 0;
8366
8367   /* copy animation control values to new field */
8368   GfxFrame[newx][newy]  = GfxFrame[x][y];
8369   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8370   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8371   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8372
8373   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8374
8375   /* some elements can leave other elements behind after moving */
8376   if (ei->move_leave_element != EL_EMPTY &&
8377       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8378       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8379   {
8380     int move_leave_element = ei->move_leave_element;
8381
8382     /* this makes it possible to leave the removed element again */
8383     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8384       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8385
8386     Feld[x][y] = move_leave_element;
8387
8388     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8389       MovDir[x][y] = direction;
8390
8391     InitField(x, y, FALSE);
8392
8393     if (GFX_CRUMBLED(Feld[x][y]))
8394       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8395
8396     if (ELEM_IS_PLAYER(move_leave_element))
8397       RelocatePlayer(x, y, move_leave_element);
8398   }
8399
8400   /* do this after checking for left-behind element */
8401   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8402
8403   if (!CAN_MOVE(element) ||
8404       (CAN_FALL(element) && direction == MV_DOWN &&
8405        (element == EL_SPRING ||
8406         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8407         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8408     GfxDir[x][y] = MovDir[newx][newy] = 0;
8409
8410   TEST_DrawLevelField(x, y);
8411   TEST_DrawLevelField(newx, newy);
8412
8413   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8414
8415   /* prevent pushed element from moving on in pushed direction */
8416   if (pushed_by_player && CAN_MOVE(element) &&
8417       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8418       !(element_info[element].move_pattern & direction))
8419     TurnRound(newx, newy);
8420
8421   /* prevent elements on conveyor belt from moving on in last direction */
8422   if (pushed_by_conveyor && CAN_FALL(element) &&
8423       direction & MV_HORIZONTAL)
8424     MovDir[newx][newy] = 0;
8425
8426   if (!pushed_by_player)
8427   {
8428     int nextx = newx + dx, nexty = newy + dy;
8429     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8430
8431     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8432
8433     if (CAN_FALL(element) && direction == MV_DOWN)
8434       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8435
8436     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8437       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8438
8439     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8440       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8441   }
8442
8443   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8444   {
8445     TestIfBadThingTouchesPlayer(newx, newy);
8446     TestIfBadThingTouchesFriend(newx, newy);
8447
8448     if (!IS_CUSTOM_ELEMENT(element))
8449       TestIfBadThingTouchesOtherBadThing(newx, newy);
8450   }
8451   else if (element == EL_PENGUIN)
8452     TestIfFriendTouchesBadThing(newx, newy);
8453
8454   if (DONT_GET_HIT_BY(element))
8455   {
8456     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8457   }
8458
8459   /* give the player one last chance (one more frame) to move away */
8460   if (CAN_FALL(element) && direction == MV_DOWN &&
8461       (last_line || (!IS_FREE(x, newy + 1) &&
8462                      (!IS_PLAYER(x, newy + 1) ||
8463                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8464     Impact(x, newy);
8465
8466   if (pushed_by_player && !game.use_change_when_pushing_bug)
8467   {
8468     int push_side = MV_DIR_OPPOSITE(direction);
8469     struct PlayerInfo *player = PLAYERINFO(x, y);
8470
8471     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8472                                player->index_bit, push_side);
8473     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8474                                         player->index_bit, push_side);
8475   }
8476
8477   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8478     MovDelay[newx][newy] = 1;
8479
8480   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8481
8482   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8483   TestIfElementHitsCustomElement(newx, newy, direction);
8484   TestIfPlayerTouchesCustomElement(newx, newy);
8485   TestIfElementTouchesCustomElement(newx, newy);
8486
8487   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8488       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8489     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8490                              MV_DIR_OPPOSITE(direction));
8491 }
8492
8493 int AmoebeNachbarNr(int ax, int ay)
8494 {
8495   int i;
8496   int element = Feld[ax][ay];
8497   int group_nr = 0;
8498   static int xy[4][2] =
8499   {
8500     { 0, -1 },
8501     { -1, 0 },
8502     { +1, 0 },
8503     { 0, +1 }
8504   };
8505
8506   for (i = 0; i < NUM_DIRECTIONS; i++)
8507   {
8508     int x = ax + xy[i][0];
8509     int y = ay + xy[i][1];
8510
8511     if (!IN_LEV_FIELD(x, y))
8512       continue;
8513
8514     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8515       group_nr = AmoebaNr[x][y];
8516   }
8517
8518   return group_nr;
8519 }
8520
8521 static void AmoebenVereinigen(int ax, int ay)
8522 {
8523   int i, x, y, xx, yy;
8524   int new_group_nr = AmoebaNr[ax][ay];
8525   static int xy[4][2] =
8526   {
8527     { 0, -1 },
8528     { -1, 0 },
8529     { +1, 0 },
8530     { 0, +1 }
8531   };
8532
8533   if (new_group_nr == 0)
8534     return;
8535
8536   for (i = 0; i < NUM_DIRECTIONS; i++)
8537   {
8538     x = ax + xy[i][0];
8539     y = ay + xy[i][1];
8540
8541     if (!IN_LEV_FIELD(x, y))
8542       continue;
8543
8544     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8545          Feld[x][y] == EL_BD_AMOEBA ||
8546          Feld[x][y] == EL_AMOEBA_DEAD) &&
8547         AmoebaNr[x][y] != new_group_nr)
8548     {
8549       int old_group_nr = AmoebaNr[x][y];
8550
8551       if (old_group_nr == 0)
8552         return;
8553
8554       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8555       AmoebaCnt[old_group_nr] = 0;
8556       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8557       AmoebaCnt2[old_group_nr] = 0;
8558
8559       SCAN_PLAYFIELD(xx, yy)
8560       {
8561         if (AmoebaNr[xx][yy] == old_group_nr)
8562           AmoebaNr[xx][yy] = new_group_nr;
8563       }
8564     }
8565   }
8566 }
8567
8568 void AmoebeUmwandeln(int ax, int ay)
8569 {
8570   int i, x, y;
8571
8572   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8573   {
8574     int group_nr = AmoebaNr[ax][ay];
8575
8576 #ifdef DEBUG
8577     if (group_nr == 0)
8578     {
8579       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8580       printf("AmoebeUmwandeln(): This should never happen!\n");
8581       return;
8582     }
8583 #endif
8584
8585     SCAN_PLAYFIELD(x, y)
8586     {
8587       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8588       {
8589         AmoebaNr[x][y] = 0;
8590         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8591       }
8592     }
8593
8594     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8595                             SND_AMOEBA_TURNING_TO_GEM :
8596                             SND_AMOEBA_TURNING_TO_ROCK));
8597     Bang(ax, ay);
8598   }
8599   else
8600   {
8601     static int xy[4][2] =
8602     {
8603       { 0, -1 },
8604       { -1, 0 },
8605       { +1, 0 },
8606       { 0, +1 }
8607     };
8608
8609     for (i = 0; i < NUM_DIRECTIONS; i++)
8610     {
8611       x = ax + xy[i][0];
8612       y = ay + xy[i][1];
8613
8614       if (!IN_LEV_FIELD(x, y))
8615         continue;
8616
8617       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8618       {
8619         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8620                               SND_AMOEBA_TURNING_TO_GEM :
8621                               SND_AMOEBA_TURNING_TO_ROCK));
8622         Bang(x, y);
8623       }
8624     }
8625   }
8626 }
8627
8628 static void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8629 {
8630   int x, y;
8631   int group_nr = AmoebaNr[ax][ay];
8632   boolean done = FALSE;
8633
8634 #ifdef DEBUG
8635   if (group_nr == 0)
8636   {
8637     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8638     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8639     return;
8640   }
8641 #endif
8642
8643   SCAN_PLAYFIELD(x, y)
8644   {
8645     if (AmoebaNr[x][y] == group_nr &&
8646         (Feld[x][y] == EL_AMOEBA_DEAD ||
8647          Feld[x][y] == EL_BD_AMOEBA ||
8648          Feld[x][y] == EL_AMOEBA_GROWING))
8649     {
8650       AmoebaNr[x][y] = 0;
8651       Feld[x][y] = new_element;
8652       InitField(x, y, FALSE);
8653       TEST_DrawLevelField(x, y);
8654       done = TRUE;
8655     }
8656   }
8657
8658   if (done)
8659     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8660                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8661                             SND_BD_AMOEBA_TURNING_TO_GEM));
8662 }
8663
8664 static void AmoebeWaechst(int x, int y)
8665 {
8666   static unsigned int sound_delay = 0;
8667   static unsigned int sound_delay_value = 0;
8668
8669   if (!MovDelay[x][y])          /* start new growing cycle */
8670   {
8671     MovDelay[x][y] = 7;
8672
8673     if (DelayReached(&sound_delay, sound_delay_value))
8674     {
8675       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8676       sound_delay_value = 30;
8677     }
8678   }
8679
8680   if (MovDelay[x][y])           /* wait some time before growing bigger */
8681   {
8682     MovDelay[x][y]--;
8683     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8684     {
8685       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8686                                            6 - MovDelay[x][y]);
8687
8688       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8689     }
8690
8691     if (!MovDelay[x][y])
8692     {
8693       Feld[x][y] = Store[x][y];
8694       Store[x][y] = 0;
8695       TEST_DrawLevelField(x, y);
8696     }
8697   }
8698 }
8699
8700 static void AmoebaDisappearing(int x, int y)
8701 {
8702   static unsigned int sound_delay = 0;
8703   static unsigned int sound_delay_value = 0;
8704
8705   if (!MovDelay[x][y])          /* start new shrinking cycle */
8706   {
8707     MovDelay[x][y] = 7;
8708
8709     if (DelayReached(&sound_delay, sound_delay_value))
8710       sound_delay_value = 30;
8711   }
8712
8713   if (MovDelay[x][y])           /* wait some time before shrinking */
8714   {
8715     MovDelay[x][y]--;
8716     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8717     {
8718       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8719                                            6 - MovDelay[x][y]);
8720
8721       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8722     }
8723
8724     if (!MovDelay[x][y])
8725     {
8726       Feld[x][y] = EL_EMPTY;
8727       TEST_DrawLevelField(x, y);
8728
8729       /* don't let mole enter this field in this cycle;
8730          (give priority to objects falling to this field from above) */
8731       Stop[x][y] = TRUE;
8732     }
8733   }
8734 }
8735
8736 static void AmoebeAbleger(int ax, int ay)
8737 {
8738   int i;
8739   int element = Feld[ax][ay];
8740   int graphic = el2img(element);
8741   int newax = ax, neway = ay;
8742   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8743   static int xy[4][2] =
8744   {
8745     { 0, -1 },
8746     { -1, 0 },
8747     { +1, 0 },
8748     { 0, +1 }
8749   };
8750
8751   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8752   {
8753     Feld[ax][ay] = EL_AMOEBA_DEAD;
8754     TEST_DrawLevelField(ax, ay);
8755     return;
8756   }
8757
8758   if (IS_ANIMATED(graphic))
8759     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8760
8761   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8762     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8763
8764   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8765   {
8766     MovDelay[ax][ay]--;
8767     if (MovDelay[ax][ay])
8768       return;
8769   }
8770
8771   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8772   {
8773     int start = RND(4);
8774     int x = ax + xy[start][0];
8775     int y = ay + xy[start][1];
8776
8777     if (!IN_LEV_FIELD(x, y))
8778       return;
8779
8780     if (IS_FREE(x, y) ||
8781         CAN_GROW_INTO(Feld[x][y]) ||
8782         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8783         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8784     {
8785       newax = x;
8786       neway = y;
8787     }
8788
8789     if (newax == ax && neway == ay)
8790       return;
8791   }
8792   else                          /* normal or "filled" (BD style) amoeba */
8793   {
8794     int start = RND(4);
8795     boolean waiting_for_player = FALSE;
8796
8797     for (i = 0; i < NUM_DIRECTIONS; i++)
8798     {
8799       int j = (start + i) % 4;
8800       int x = ax + xy[j][0];
8801       int y = ay + xy[j][1];
8802
8803       if (!IN_LEV_FIELD(x, y))
8804         continue;
8805
8806       if (IS_FREE(x, y) ||
8807           CAN_GROW_INTO(Feld[x][y]) ||
8808           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8809           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8810       {
8811         newax = x;
8812         neway = y;
8813         break;
8814       }
8815       else if (IS_PLAYER(x, y))
8816         waiting_for_player = TRUE;
8817     }
8818
8819     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8820     {
8821       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8822       {
8823         Feld[ax][ay] = EL_AMOEBA_DEAD;
8824         TEST_DrawLevelField(ax, ay);
8825         AmoebaCnt[AmoebaNr[ax][ay]]--;
8826
8827         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8828         {
8829           if (element == EL_AMOEBA_FULL)
8830             AmoebeUmwandeln(ax, ay);
8831           else if (element == EL_BD_AMOEBA)
8832             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8833         }
8834       }
8835       return;
8836     }
8837     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8838     {
8839       /* amoeba gets larger by growing in some direction */
8840
8841       int new_group_nr = AmoebaNr[ax][ay];
8842
8843 #ifdef DEBUG
8844   if (new_group_nr == 0)
8845   {
8846     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8847     printf("AmoebeAbleger(): This should never happen!\n");
8848     return;
8849   }
8850 #endif
8851
8852       AmoebaNr[newax][neway] = new_group_nr;
8853       AmoebaCnt[new_group_nr]++;
8854       AmoebaCnt2[new_group_nr]++;
8855
8856       /* if amoeba touches other amoeba(s) after growing, unify them */
8857       AmoebenVereinigen(newax, neway);
8858
8859       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8860       {
8861         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8862         return;
8863       }
8864     }
8865   }
8866
8867   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8868       (neway == lev_fieldy - 1 && newax != ax))
8869   {
8870     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8871     Store[newax][neway] = element;
8872   }
8873   else if (neway == ay || element == EL_EMC_DRIPPER)
8874   {
8875     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8876
8877     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8878   }
8879   else
8880   {
8881     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8882     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8883     Store[ax][ay] = EL_AMOEBA_DROP;
8884     ContinueMoving(ax, ay);
8885     return;
8886   }
8887
8888   TEST_DrawLevelField(newax, neway);
8889 }
8890
8891 static void Life(int ax, int ay)
8892 {
8893   int x1, y1, x2, y2;
8894   int life_time = 40;
8895   int element = Feld[ax][ay];
8896   int graphic = el2img(element);
8897   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8898                          level.biomaze);
8899   boolean changed = FALSE;
8900
8901   if (IS_ANIMATED(graphic))
8902     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8903
8904   if (Stop[ax][ay])
8905     return;
8906
8907   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8908     MovDelay[ax][ay] = life_time;
8909
8910   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8911   {
8912     MovDelay[ax][ay]--;
8913     if (MovDelay[ax][ay])
8914       return;
8915   }
8916
8917   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8918   {
8919     int xx = ax+x1, yy = ay+y1;
8920     int old_element = Feld[xx][yy];
8921     int num_neighbours = 0;
8922
8923     if (!IN_LEV_FIELD(xx, yy))
8924       continue;
8925
8926     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8927     {
8928       int x = xx+x2, y = yy+y2;
8929
8930       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8931         continue;
8932
8933       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
8934       boolean is_neighbour = FALSE;
8935
8936       if (level.use_life_bugs)
8937         is_neighbour =
8938           (((Feld[x][y] == element || is_player_cell) && !Stop[x][y]) ||
8939            (IS_FREE(x, y)                             &&  Stop[x][y]));
8940       else
8941         is_neighbour =
8942           (Last[x][y] == element || is_player_cell);
8943
8944       if (is_neighbour)
8945         num_neighbours++;
8946     }
8947
8948     boolean is_free = FALSE;
8949
8950     if (level.use_life_bugs)
8951       is_free = (IS_FREE(xx, yy));
8952     else
8953       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
8954
8955     if (xx == ax && yy == ay)           /* field in the middle */
8956     {
8957       if (num_neighbours < life_parameter[0] ||
8958           num_neighbours > life_parameter[1])
8959       {
8960         Feld[xx][yy] = EL_EMPTY;
8961         if (Feld[xx][yy] != old_element)
8962           TEST_DrawLevelField(xx, yy);
8963         Stop[xx][yy] = TRUE;
8964         changed = TRUE;
8965       }
8966     }
8967     else if (is_free || CAN_GROW_INTO(Feld[xx][yy]))
8968     {                                   /* free border field */
8969       if (num_neighbours >= life_parameter[2] &&
8970           num_neighbours <= life_parameter[3])
8971       {
8972         Feld[xx][yy] = element;
8973         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8974         if (Feld[xx][yy] != old_element)
8975           TEST_DrawLevelField(xx, yy);
8976         Stop[xx][yy] = TRUE;
8977         changed = TRUE;
8978       }
8979     }
8980   }
8981
8982   if (changed)
8983     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8984                    SND_GAME_OF_LIFE_GROWING);
8985 }
8986
8987 static void InitRobotWheel(int x, int y)
8988 {
8989   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8990 }
8991
8992 static void RunRobotWheel(int x, int y)
8993 {
8994   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8995 }
8996
8997 static void StopRobotWheel(int x, int y)
8998 {
8999   if (ZX == x && ZY == y)
9000   {
9001     ZX = ZY = -1;
9002
9003     game.robot_wheel_active = FALSE;
9004   }
9005 }
9006
9007 static void InitTimegateWheel(int x, int y)
9008 {
9009   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9010 }
9011
9012 static void RunTimegateWheel(int x, int y)
9013 {
9014   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9015 }
9016
9017 static void InitMagicBallDelay(int x, int y)
9018 {
9019   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9020 }
9021
9022 static void ActivateMagicBall(int bx, int by)
9023 {
9024   int x, y;
9025
9026   if (level.ball_random)
9027   {
9028     int pos_border = RND(8);    /* select one of the eight border elements */
9029     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9030     int xx = pos_content % 3;
9031     int yy = pos_content / 3;
9032
9033     x = bx - 1 + xx;
9034     y = by - 1 + yy;
9035
9036     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9037       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9038   }
9039   else
9040   {
9041     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9042     {
9043       int xx = x - bx + 1;
9044       int yy = y - by + 1;
9045
9046       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9047         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9048     }
9049   }
9050
9051   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9052 }
9053
9054 static void CheckExit(int x, int y)
9055 {
9056   if (local_player->gems_still_needed > 0 ||
9057       local_player->sokobanfields_still_needed > 0 ||
9058       local_player->lights_still_needed > 0)
9059   {
9060     int element = Feld[x][y];
9061     int graphic = el2img(element);
9062
9063     if (IS_ANIMATED(graphic))
9064       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9065
9066     return;
9067   }
9068
9069   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9070     return;
9071
9072   Feld[x][y] = EL_EXIT_OPENING;
9073
9074   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9075 }
9076
9077 static void CheckExitEM(int x, int y)
9078 {
9079   if (local_player->gems_still_needed > 0 ||
9080       local_player->sokobanfields_still_needed > 0 ||
9081       local_player->lights_still_needed > 0)
9082   {
9083     int element = Feld[x][y];
9084     int graphic = el2img(element);
9085
9086     if (IS_ANIMATED(graphic))
9087       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9088
9089     return;
9090   }
9091
9092   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9093     return;
9094
9095   Feld[x][y] = EL_EM_EXIT_OPENING;
9096
9097   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9098 }
9099
9100 static void CheckExitSteel(int x, int y)
9101 {
9102   if (local_player->gems_still_needed > 0 ||
9103       local_player->sokobanfields_still_needed > 0 ||
9104       local_player->lights_still_needed > 0)
9105   {
9106     int element = Feld[x][y];
9107     int graphic = el2img(element);
9108
9109     if (IS_ANIMATED(graphic))
9110       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9111
9112     return;
9113   }
9114
9115   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9116     return;
9117
9118   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9119
9120   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9121 }
9122
9123 static void CheckExitSteelEM(int x, int y)
9124 {
9125   if (local_player->gems_still_needed > 0 ||
9126       local_player->sokobanfields_still_needed > 0 ||
9127       local_player->lights_still_needed > 0)
9128   {
9129     int element = Feld[x][y];
9130     int graphic = el2img(element);
9131
9132     if (IS_ANIMATED(graphic))
9133       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9134
9135     return;
9136   }
9137
9138   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9139     return;
9140
9141   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9142
9143   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9144 }
9145
9146 static void CheckExitSP(int x, int y)
9147 {
9148   if (local_player->gems_still_needed > 0)
9149   {
9150     int element = Feld[x][y];
9151     int graphic = el2img(element);
9152
9153     if (IS_ANIMATED(graphic))
9154       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9155
9156     return;
9157   }
9158
9159   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9160     return;
9161
9162   Feld[x][y] = EL_SP_EXIT_OPENING;
9163
9164   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9165 }
9166
9167 static void CloseAllOpenTimegates(void)
9168 {
9169   int x, y;
9170
9171   SCAN_PLAYFIELD(x, y)
9172   {
9173     int element = Feld[x][y];
9174
9175     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9176     {
9177       Feld[x][y] = EL_TIMEGATE_CLOSING;
9178
9179       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9180     }
9181   }
9182 }
9183
9184 static void DrawTwinkleOnField(int x, int y)
9185 {
9186   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9187     return;
9188
9189   if (Feld[x][y] == EL_BD_DIAMOND)
9190     return;
9191
9192   if (MovDelay[x][y] == 0)      /* next animation frame */
9193     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9194
9195   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9196   {
9197     MovDelay[x][y]--;
9198
9199     DrawLevelElementAnimation(x, y, Feld[x][y]);
9200
9201     if (MovDelay[x][y] != 0)
9202     {
9203       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9204                                            10 - MovDelay[x][y]);
9205
9206       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9207     }
9208   }
9209 }
9210
9211 static void MauerWaechst(int x, int y)
9212 {
9213   int delay = 6;
9214
9215   if (!MovDelay[x][y])          /* next animation frame */
9216     MovDelay[x][y] = 3 * delay;
9217
9218   if (MovDelay[x][y])           /* wait some time before next frame */
9219   {
9220     MovDelay[x][y]--;
9221
9222     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9223     {
9224       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9225       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9226
9227       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9228     }
9229
9230     if (!MovDelay[x][y])
9231     {
9232       if (MovDir[x][y] == MV_LEFT)
9233       {
9234         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9235           TEST_DrawLevelField(x - 1, y);
9236       }
9237       else if (MovDir[x][y] == MV_RIGHT)
9238       {
9239         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9240           TEST_DrawLevelField(x + 1, y);
9241       }
9242       else if (MovDir[x][y] == MV_UP)
9243       {
9244         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9245           TEST_DrawLevelField(x, y - 1);
9246       }
9247       else
9248       {
9249         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9250           TEST_DrawLevelField(x, y + 1);
9251       }
9252
9253       Feld[x][y] = Store[x][y];
9254       Store[x][y] = 0;
9255       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9256       TEST_DrawLevelField(x, y);
9257     }
9258   }
9259 }
9260
9261 static void MauerAbleger(int ax, int ay)
9262 {
9263   int element = Feld[ax][ay];
9264   int graphic = el2img(element);
9265   boolean oben_frei = FALSE, unten_frei = FALSE;
9266   boolean links_frei = FALSE, rechts_frei = FALSE;
9267   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9268   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9269   boolean new_wall = FALSE;
9270
9271   if (IS_ANIMATED(graphic))
9272     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9273
9274   if (!MovDelay[ax][ay])        /* start building new wall */
9275     MovDelay[ax][ay] = 6;
9276
9277   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9278   {
9279     MovDelay[ax][ay]--;
9280     if (MovDelay[ax][ay])
9281       return;
9282   }
9283
9284   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9285     oben_frei = TRUE;
9286   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9287     unten_frei = TRUE;
9288   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9289     links_frei = TRUE;
9290   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9291     rechts_frei = TRUE;
9292
9293   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9294       element == EL_EXPANDABLE_WALL_ANY)
9295   {
9296     if (oben_frei)
9297     {
9298       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9299       Store[ax][ay-1] = element;
9300       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9301       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9302         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9303                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9304       new_wall = TRUE;
9305     }
9306     if (unten_frei)
9307     {
9308       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9309       Store[ax][ay+1] = element;
9310       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9311       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9312         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9313                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9314       new_wall = TRUE;
9315     }
9316   }
9317
9318   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9319       element == EL_EXPANDABLE_WALL_ANY ||
9320       element == EL_EXPANDABLE_WALL ||
9321       element == EL_BD_EXPANDABLE_WALL)
9322   {
9323     if (links_frei)
9324     {
9325       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9326       Store[ax-1][ay] = element;
9327       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9328       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9329         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9330                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9331       new_wall = TRUE;
9332     }
9333
9334     if (rechts_frei)
9335     {
9336       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9337       Store[ax+1][ay] = element;
9338       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9339       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9340         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9341                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9342       new_wall = TRUE;
9343     }
9344   }
9345
9346   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9347     TEST_DrawLevelField(ax, ay);
9348
9349   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9350     oben_massiv = TRUE;
9351   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9352     unten_massiv = TRUE;
9353   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9354     links_massiv = TRUE;
9355   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9356     rechts_massiv = TRUE;
9357
9358   if (((oben_massiv && unten_massiv) ||
9359        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9360        element == EL_EXPANDABLE_WALL) &&
9361       ((links_massiv && rechts_massiv) ||
9362        element == EL_EXPANDABLE_WALL_VERTICAL))
9363     Feld[ax][ay] = EL_WALL;
9364
9365   if (new_wall)
9366     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9367 }
9368
9369 static void MauerAblegerStahl(int ax, int ay)
9370 {
9371   int element = Feld[ax][ay];
9372   int graphic = el2img(element);
9373   boolean oben_frei = FALSE, unten_frei = FALSE;
9374   boolean links_frei = FALSE, rechts_frei = FALSE;
9375   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9376   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9377   boolean new_wall = FALSE;
9378
9379   if (IS_ANIMATED(graphic))
9380     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9381
9382   if (!MovDelay[ax][ay])        /* start building new wall */
9383     MovDelay[ax][ay] = 6;
9384
9385   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9386   {
9387     MovDelay[ax][ay]--;
9388     if (MovDelay[ax][ay])
9389       return;
9390   }
9391
9392   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9393     oben_frei = TRUE;
9394   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9395     unten_frei = TRUE;
9396   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9397     links_frei = TRUE;
9398   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9399     rechts_frei = TRUE;
9400
9401   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9402       element == EL_EXPANDABLE_STEELWALL_ANY)
9403   {
9404     if (oben_frei)
9405     {
9406       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9407       Store[ax][ay-1] = element;
9408       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9409       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9410         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9411                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9412       new_wall = TRUE;
9413     }
9414     if (unten_frei)
9415     {
9416       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9417       Store[ax][ay+1] = element;
9418       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9419       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9420         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9421                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9422       new_wall = TRUE;
9423     }
9424   }
9425
9426   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9427       element == EL_EXPANDABLE_STEELWALL_ANY)
9428   {
9429     if (links_frei)
9430     {
9431       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9432       Store[ax-1][ay] = element;
9433       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9434       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9435         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9436                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9437       new_wall = TRUE;
9438     }
9439
9440     if (rechts_frei)
9441     {
9442       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9443       Store[ax+1][ay] = element;
9444       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9445       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9446         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9447                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9448       new_wall = TRUE;
9449     }
9450   }
9451
9452   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9453     oben_massiv = TRUE;
9454   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9455     unten_massiv = TRUE;
9456   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9457     links_massiv = TRUE;
9458   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9459     rechts_massiv = TRUE;
9460
9461   if (((oben_massiv && unten_massiv) ||
9462        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9463       ((links_massiv && rechts_massiv) ||
9464        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9465     Feld[ax][ay] = EL_STEELWALL;
9466
9467   if (new_wall)
9468     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9469 }
9470
9471 static void CheckForDragon(int x, int y)
9472 {
9473   int i, j;
9474   boolean dragon_found = FALSE;
9475   static int xy[4][2] =
9476   {
9477     { 0, -1 },
9478     { -1, 0 },
9479     { +1, 0 },
9480     { 0, +1 }
9481   };
9482
9483   for (i = 0; i < NUM_DIRECTIONS; i++)
9484   {
9485     for (j = 0; j < 4; j++)
9486     {
9487       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9488
9489       if (IN_LEV_FIELD(xx, yy) &&
9490           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9491       {
9492         if (Feld[xx][yy] == EL_DRAGON)
9493           dragon_found = TRUE;
9494       }
9495       else
9496         break;
9497     }
9498   }
9499
9500   if (!dragon_found)
9501   {
9502     for (i = 0; i < NUM_DIRECTIONS; i++)
9503     {
9504       for (j = 0; j < 3; j++)
9505       {
9506         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9507   
9508         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9509         {
9510           Feld[xx][yy] = EL_EMPTY;
9511           TEST_DrawLevelField(xx, yy);
9512         }
9513         else
9514           break;
9515       }
9516     }
9517   }
9518 }
9519
9520 static void InitBuggyBase(int x, int y)
9521 {
9522   int element = Feld[x][y];
9523   int activating_delay = FRAMES_PER_SECOND / 4;
9524
9525   ChangeDelay[x][y] =
9526     (element == EL_SP_BUGGY_BASE ?
9527      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9528      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9529      activating_delay :
9530      element == EL_SP_BUGGY_BASE_ACTIVE ?
9531      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9532 }
9533
9534 static void WarnBuggyBase(int x, int y)
9535 {
9536   int i;
9537   static int xy[4][2] =
9538   {
9539     { 0, -1 },
9540     { -1, 0 },
9541     { +1, 0 },
9542     { 0, +1 }
9543   };
9544
9545   for (i = 0; i < NUM_DIRECTIONS; i++)
9546   {
9547     int xx = x + xy[i][0];
9548     int yy = y + xy[i][1];
9549
9550     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9551     {
9552       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9553
9554       break;
9555     }
9556   }
9557 }
9558
9559 static void InitTrap(int x, int y)
9560 {
9561   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9562 }
9563
9564 static void ActivateTrap(int x, int y)
9565 {
9566   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9567 }
9568
9569 static void ChangeActiveTrap(int x, int y)
9570 {
9571   int graphic = IMG_TRAP_ACTIVE;
9572
9573   /* if new animation frame was drawn, correct crumbled sand border */
9574   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9575     TEST_DrawLevelFieldCrumbled(x, y);
9576 }
9577
9578 static int getSpecialActionElement(int element, int number, int base_element)
9579 {
9580   return (element != EL_EMPTY ? element :
9581           number != -1 ? base_element + number - 1 :
9582           EL_EMPTY);
9583 }
9584
9585 static int getModifiedActionNumber(int value_old, int operator, int operand,
9586                                    int value_min, int value_max)
9587 {
9588   int value_new = (operator == CA_MODE_SET      ? operand :
9589                    operator == CA_MODE_ADD      ? value_old + operand :
9590                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9591                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9592                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9593                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9594                    value_old);
9595
9596   return (value_new < value_min ? value_min :
9597           value_new > value_max ? value_max :
9598           value_new);
9599 }
9600
9601 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9602 {
9603   struct ElementInfo *ei = &element_info[element];
9604   struct ElementChangeInfo *change = &ei->change_page[page];
9605   int target_element = change->target_element;
9606   int action_type = change->action_type;
9607   int action_mode = change->action_mode;
9608   int action_arg = change->action_arg;
9609   int action_element = change->action_element;
9610   int i;
9611
9612   if (!change->has_action)
9613     return;
9614
9615   /* ---------- determine action paramater values -------------------------- */
9616
9617   int level_time_value =
9618     (level.time > 0 ? TimeLeft :
9619      TimePlayed);
9620
9621   int action_arg_element_raw =
9622     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9623      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9624      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9625      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9626      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9627      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9628      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9629      EL_EMPTY);
9630   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9631
9632   int action_arg_direction =
9633     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9634      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9635      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9636      change->actual_trigger_side :
9637      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9638      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9639      MV_NONE);
9640
9641   int action_arg_number_min =
9642     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9643      CA_ARG_MIN);
9644
9645   int action_arg_number_max =
9646     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9647      action_type == CA_SET_LEVEL_GEMS ? 999 :
9648      action_type == CA_SET_LEVEL_TIME ? 9999 :
9649      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9650      action_type == CA_SET_CE_VALUE ? 9999 :
9651      action_type == CA_SET_CE_SCORE ? 9999 :
9652      CA_ARG_MAX);
9653
9654   int action_arg_number_reset =
9655     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9656      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9657      action_type == CA_SET_LEVEL_TIME ? level.time :
9658      action_type == CA_SET_LEVEL_SCORE ? 0 :
9659      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9660      action_type == CA_SET_CE_SCORE ? 0 :
9661      0);
9662
9663   int action_arg_number =
9664     (action_arg <= CA_ARG_MAX ? action_arg :
9665      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9666      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9667      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9668      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9669      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9670      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9671      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9672      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9673      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9674      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9675      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9676      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9677      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9678      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9679      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9680      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9681      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9682      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9683      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9684      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9685      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9686      -1);
9687
9688   int action_arg_number_old =
9689     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9690      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9691      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9692      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9693      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9694      0);
9695
9696   int action_arg_number_new =
9697     getModifiedActionNumber(action_arg_number_old,
9698                             action_mode, action_arg_number,
9699                             action_arg_number_min, action_arg_number_max);
9700
9701   int trigger_player_bits =
9702     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9703      change->actual_trigger_player_bits : change->trigger_player);
9704
9705   int action_arg_player_bits =
9706     (action_arg >= CA_ARG_PLAYER_1 &&
9707      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9708      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9709      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9710      PLAYER_BITS_ANY);
9711
9712   /* ---------- execute action  -------------------------------------------- */
9713
9714   switch (action_type)
9715   {
9716     case CA_NO_ACTION:
9717     {
9718       return;
9719     }
9720
9721     /* ---------- level actions  ------------------------------------------- */
9722
9723     case CA_RESTART_LEVEL:
9724     {
9725       game.restart_level = TRUE;
9726
9727       break;
9728     }
9729
9730     case CA_SHOW_ENVELOPE:
9731     {
9732       int element = getSpecialActionElement(action_arg_element,
9733                                             action_arg_number, EL_ENVELOPE_1);
9734
9735       if (IS_ENVELOPE(element))
9736         local_player->show_envelope = element;
9737
9738       break;
9739     }
9740
9741     case CA_SET_LEVEL_TIME:
9742     {
9743       if (level.time > 0)       /* only modify limited time value */
9744       {
9745         TimeLeft = action_arg_number_new;
9746
9747         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9748
9749         DisplayGameControlValues();
9750
9751         if (!TimeLeft && setup.time_limit)
9752           for (i = 0; i < MAX_PLAYERS; i++)
9753             KillPlayer(&stored_player[i]);
9754       }
9755
9756       break;
9757     }
9758
9759     case CA_SET_LEVEL_SCORE:
9760     {
9761       local_player->score = action_arg_number_new;
9762
9763       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9764
9765       DisplayGameControlValues();
9766
9767       break;
9768     }
9769
9770     case CA_SET_LEVEL_GEMS:
9771     {
9772       local_player->gems_still_needed = action_arg_number_new;
9773
9774       game.snapshot.collected_item = TRUE;
9775
9776       game_panel_controls[GAME_PANEL_GEMS].value =
9777         local_player->gems_still_needed;
9778
9779       DisplayGameControlValues();
9780
9781       break;
9782     }
9783
9784     case CA_SET_LEVEL_WIND:
9785     {
9786       game.wind_direction = action_arg_direction;
9787
9788       break;
9789     }
9790
9791     case CA_SET_LEVEL_RANDOM_SEED:
9792     {
9793       /* ensure that setting a new random seed while playing is predictable */
9794       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9795
9796       break;
9797     }
9798
9799     /* ---------- player actions  ------------------------------------------ */
9800
9801     case CA_MOVE_PLAYER:
9802     {
9803       /* automatically move to the next field in specified direction */
9804       for (i = 0; i < MAX_PLAYERS; i++)
9805         if (trigger_player_bits & (1 << i))
9806           stored_player[i].programmed_action = action_arg_direction;
9807
9808       break;
9809     }
9810
9811     case CA_EXIT_PLAYER:
9812     {
9813       for (i = 0; i < MAX_PLAYERS; i++)
9814         if (action_arg_player_bits & (1 << i))
9815           ExitPlayer(&stored_player[i]);
9816
9817       if (AllPlayersGone)
9818         PlayerWins(local_player);
9819
9820       break;
9821     }
9822
9823     case CA_KILL_PLAYER:
9824     {
9825       for (i = 0; i < MAX_PLAYERS; i++)
9826         if (action_arg_player_bits & (1 << i))
9827           KillPlayer(&stored_player[i]);
9828
9829       break;
9830     }
9831
9832     case CA_SET_PLAYER_KEYS:
9833     {
9834       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9835       int element = getSpecialActionElement(action_arg_element,
9836                                             action_arg_number, EL_KEY_1);
9837
9838       if (IS_KEY(element))
9839       {
9840         for (i = 0; i < MAX_PLAYERS; i++)
9841         {
9842           if (trigger_player_bits & (1 << i))
9843           {
9844             stored_player[i].key[KEY_NR(element)] = key_state;
9845
9846             DrawGameDoorValues();
9847           }
9848         }
9849       }
9850
9851       break;
9852     }
9853
9854     case CA_SET_PLAYER_SPEED:
9855     {
9856       for (i = 0; i < MAX_PLAYERS; i++)
9857       {
9858         if (trigger_player_bits & (1 << i))
9859         {
9860           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9861
9862           if (action_arg == CA_ARG_SPEED_FASTER &&
9863               stored_player[i].cannot_move)
9864           {
9865             action_arg_number = STEPSIZE_VERY_SLOW;
9866           }
9867           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9868                    action_arg == CA_ARG_SPEED_FASTER)
9869           {
9870             action_arg_number = 2;
9871             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9872                            CA_MODE_MULTIPLY);
9873           }
9874           else if (action_arg == CA_ARG_NUMBER_RESET)
9875           {
9876             action_arg_number = level.initial_player_stepsize[i];
9877           }
9878
9879           move_stepsize =
9880             getModifiedActionNumber(move_stepsize,
9881                                     action_mode,
9882                                     action_arg_number,
9883                                     action_arg_number_min,
9884                                     action_arg_number_max);
9885
9886           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9887         }
9888       }
9889
9890       break;
9891     }
9892
9893     case CA_SET_PLAYER_SHIELD:
9894     {
9895       for (i = 0; i < MAX_PLAYERS; i++)
9896       {
9897         if (trigger_player_bits & (1 << i))
9898         {
9899           if (action_arg == CA_ARG_SHIELD_OFF)
9900           {
9901             stored_player[i].shield_normal_time_left = 0;
9902             stored_player[i].shield_deadly_time_left = 0;
9903           }
9904           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9905           {
9906             stored_player[i].shield_normal_time_left = 999999;
9907           }
9908           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9909           {
9910             stored_player[i].shield_normal_time_left = 999999;
9911             stored_player[i].shield_deadly_time_left = 999999;
9912           }
9913         }
9914       }
9915
9916       break;
9917     }
9918
9919     case CA_SET_PLAYER_GRAVITY:
9920     {
9921       for (i = 0; i < MAX_PLAYERS; i++)
9922       {
9923         if (trigger_player_bits & (1 << i))
9924         {
9925           stored_player[i].gravity =
9926             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9927              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9928              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9929              stored_player[i].gravity);
9930         }
9931       }
9932
9933       break;
9934     }
9935
9936     case CA_SET_PLAYER_ARTWORK:
9937     {
9938       for (i = 0; i < MAX_PLAYERS; i++)
9939       {
9940         if (trigger_player_bits & (1 << i))
9941         {
9942           int artwork_element = action_arg_element;
9943
9944           if (action_arg == CA_ARG_ELEMENT_RESET)
9945             artwork_element =
9946               (level.use_artwork_element[i] ? level.artwork_element[i] :
9947                stored_player[i].element_nr);
9948
9949           if (stored_player[i].artwork_element != artwork_element)
9950             stored_player[i].Frame = 0;
9951
9952           stored_player[i].artwork_element = artwork_element;
9953
9954           SetPlayerWaiting(&stored_player[i], FALSE);
9955
9956           /* set number of special actions for bored and sleeping animation */
9957           stored_player[i].num_special_action_bored =
9958             get_num_special_action(artwork_element,
9959                                    ACTION_BORING_1, ACTION_BORING_LAST);
9960           stored_player[i].num_special_action_sleeping =
9961             get_num_special_action(artwork_element,
9962                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9963         }
9964       }
9965
9966       break;
9967     }
9968
9969     case CA_SET_PLAYER_INVENTORY:
9970     {
9971       for (i = 0; i < MAX_PLAYERS; i++)
9972       {
9973         struct PlayerInfo *player = &stored_player[i];
9974         int j, k;
9975
9976         if (trigger_player_bits & (1 << i))
9977         {
9978           int inventory_element = action_arg_element;
9979
9980           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9981               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9982               action_arg == CA_ARG_ELEMENT_ACTION)
9983           {
9984             int element = inventory_element;
9985             int collect_count = element_info[element].collect_count_initial;
9986
9987             if (!IS_CUSTOM_ELEMENT(element))
9988               collect_count = 1;
9989
9990             if (collect_count == 0)
9991               player->inventory_infinite_element = element;
9992             else
9993               for (k = 0; k < collect_count; k++)
9994                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9995                   player->inventory_element[player->inventory_size++] =
9996                     element;
9997           }
9998           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9999                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10000                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10001           {
10002             if (player->inventory_infinite_element != EL_UNDEFINED &&
10003                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10004                                      action_arg_element_raw))
10005               player->inventory_infinite_element = EL_UNDEFINED;
10006
10007             for (k = 0, j = 0; j < player->inventory_size; j++)
10008             {
10009               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10010                                         action_arg_element_raw))
10011                 player->inventory_element[k++] = player->inventory_element[j];
10012             }
10013
10014             player->inventory_size = k;
10015           }
10016           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10017           {
10018             if (player->inventory_size > 0)
10019             {
10020               for (j = 0; j < player->inventory_size - 1; j++)
10021                 player->inventory_element[j] = player->inventory_element[j + 1];
10022
10023               player->inventory_size--;
10024             }
10025           }
10026           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10027           {
10028             if (player->inventory_size > 0)
10029               player->inventory_size--;
10030           }
10031           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10032           {
10033             player->inventory_infinite_element = EL_UNDEFINED;
10034             player->inventory_size = 0;
10035           }
10036           else if (action_arg == CA_ARG_INVENTORY_RESET)
10037           {
10038             player->inventory_infinite_element = EL_UNDEFINED;
10039             player->inventory_size = 0;
10040
10041             if (level.use_initial_inventory[i])
10042             {
10043               for (j = 0; j < level.initial_inventory_size[i]; j++)
10044               {
10045                 int element = level.initial_inventory_content[i][j];
10046                 int collect_count = element_info[element].collect_count_initial;
10047
10048                 if (!IS_CUSTOM_ELEMENT(element))
10049                   collect_count = 1;
10050
10051                 if (collect_count == 0)
10052                   player->inventory_infinite_element = element;
10053                 else
10054                   for (k = 0; k < collect_count; k++)
10055                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10056                       player->inventory_element[player->inventory_size++] =
10057                         element;
10058               }
10059             }
10060           }
10061         }
10062       }
10063
10064       break;
10065     }
10066
10067     /* ---------- CE actions  ---------------------------------------------- */
10068
10069     case CA_SET_CE_VALUE:
10070     {
10071       int last_ce_value = CustomValue[x][y];
10072
10073       CustomValue[x][y] = action_arg_number_new;
10074
10075       if (CustomValue[x][y] != last_ce_value)
10076       {
10077         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10078         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10079
10080         if (CustomValue[x][y] == 0)
10081         {
10082           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10083           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10084         }
10085       }
10086
10087       break;
10088     }
10089
10090     case CA_SET_CE_SCORE:
10091     {
10092       int last_ce_score = ei->collect_score;
10093
10094       ei->collect_score = action_arg_number_new;
10095
10096       if (ei->collect_score != last_ce_score)
10097       {
10098         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10099         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10100
10101         if (ei->collect_score == 0)
10102         {
10103           int xx, yy;
10104
10105           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10106           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10107
10108           /*
10109             This is a very special case that seems to be a mixture between
10110             CheckElementChange() and CheckTriggeredElementChange(): while
10111             the first one only affects single elements that are triggered
10112             directly, the second one affects multiple elements in the playfield
10113             that are triggered indirectly by another element. This is a third
10114             case: Changing the CE score always affects multiple identical CEs,
10115             so every affected CE must be checked, not only the single CE for
10116             which the CE score was changed in the first place (as every instance
10117             of that CE shares the same CE score, and therefore also can change)!
10118           */
10119           SCAN_PLAYFIELD(xx, yy)
10120           {
10121             if (Feld[xx][yy] == element)
10122               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10123                                  CE_SCORE_GETS_ZERO);
10124           }
10125         }
10126       }
10127
10128       break;
10129     }
10130
10131     case CA_SET_CE_ARTWORK:
10132     {
10133       int artwork_element = action_arg_element;
10134       boolean reset_frame = FALSE;
10135       int xx, yy;
10136
10137       if (action_arg == CA_ARG_ELEMENT_RESET)
10138         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10139                            element);
10140
10141       if (ei->gfx_element != artwork_element)
10142         reset_frame = TRUE;
10143
10144       ei->gfx_element = artwork_element;
10145
10146       SCAN_PLAYFIELD(xx, yy)
10147       {
10148         if (Feld[xx][yy] == element)
10149         {
10150           if (reset_frame)
10151           {
10152             ResetGfxAnimation(xx, yy);
10153             ResetRandomAnimationValue(xx, yy);
10154           }
10155
10156           TEST_DrawLevelField(xx, yy);
10157         }
10158       }
10159
10160       break;
10161     }
10162
10163     /* ---------- engine actions  ------------------------------------------ */
10164
10165     case CA_SET_ENGINE_SCAN_MODE:
10166     {
10167       InitPlayfieldScanMode(action_arg);
10168
10169       break;
10170     }
10171
10172     default:
10173       break;
10174   }
10175 }
10176
10177 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10178 {
10179   int old_element = Feld[x][y];
10180   int new_element = GetElementFromGroupElement(element);
10181   int previous_move_direction = MovDir[x][y];
10182   int last_ce_value = CustomValue[x][y];
10183   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10184   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10185   boolean add_player_onto_element = (new_element_is_player &&
10186                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10187                                      IS_WALKABLE(old_element));
10188
10189   if (!add_player_onto_element)
10190   {
10191     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10192       RemoveMovingField(x, y);
10193     else
10194       RemoveField(x, y);
10195
10196     Feld[x][y] = new_element;
10197
10198     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10199       MovDir[x][y] = previous_move_direction;
10200
10201     if (element_info[new_element].use_last_ce_value)
10202       CustomValue[x][y] = last_ce_value;
10203
10204     InitField_WithBug1(x, y, FALSE);
10205
10206     new_element = Feld[x][y];   /* element may have changed */
10207
10208     ResetGfxAnimation(x, y);
10209     ResetRandomAnimationValue(x, y);
10210
10211     TEST_DrawLevelField(x, y);
10212
10213     if (GFX_CRUMBLED(new_element))
10214       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10215   }
10216
10217   /* check if element under the player changes from accessible to unaccessible
10218      (needed for special case of dropping element which then changes) */
10219   /* (must be checked after creating new element for walkable group elements) */
10220   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10221       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10222   {
10223     Bang(x, y);
10224
10225     return;
10226   }
10227
10228   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10229   if (new_element_is_player)
10230     RelocatePlayer(x, y, new_element);
10231
10232   if (is_change)
10233     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10234
10235   TestIfBadThingTouchesPlayer(x, y);
10236   TestIfPlayerTouchesCustomElement(x, y);
10237   TestIfElementTouchesCustomElement(x, y);
10238 }
10239
10240 static void CreateField(int x, int y, int element)
10241 {
10242   CreateFieldExt(x, y, element, FALSE);
10243 }
10244
10245 static void CreateElementFromChange(int x, int y, int element)
10246 {
10247   element = GET_VALID_RUNTIME_ELEMENT(element);
10248
10249   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10250   {
10251     int old_element = Feld[x][y];
10252
10253     /* prevent changed element from moving in same engine frame
10254        unless both old and new element can either fall or move */
10255     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10256         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10257       Stop[x][y] = TRUE;
10258   }
10259
10260   CreateFieldExt(x, y, element, TRUE);
10261 }
10262
10263 static boolean ChangeElement(int x, int y, int element, int page)
10264 {
10265   struct ElementInfo *ei = &element_info[element];
10266   struct ElementChangeInfo *change = &ei->change_page[page];
10267   int ce_value = CustomValue[x][y];
10268   int ce_score = ei->collect_score;
10269   int target_element;
10270   int old_element = Feld[x][y];
10271
10272   /* always use default change event to prevent running into a loop */
10273   if (ChangeEvent[x][y] == -1)
10274     ChangeEvent[x][y] = CE_DELAY;
10275
10276   if (ChangeEvent[x][y] == CE_DELAY)
10277   {
10278     /* reset actual trigger element, trigger player and action element */
10279     change->actual_trigger_element = EL_EMPTY;
10280     change->actual_trigger_player = EL_EMPTY;
10281     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10282     change->actual_trigger_side = CH_SIDE_NONE;
10283     change->actual_trigger_ce_value = 0;
10284     change->actual_trigger_ce_score = 0;
10285   }
10286
10287   /* do not change elements more than a specified maximum number of changes */
10288   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10289     return FALSE;
10290
10291   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10292
10293   if (change->explode)
10294   {
10295     Bang(x, y);
10296
10297     return TRUE;
10298   }
10299
10300   if (change->use_target_content)
10301   {
10302     boolean complete_replace = TRUE;
10303     boolean can_replace[3][3];
10304     int xx, yy;
10305
10306     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10307     {
10308       boolean is_empty;
10309       boolean is_walkable;
10310       boolean is_diggable;
10311       boolean is_collectible;
10312       boolean is_removable;
10313       boolean is_destructible;
10314       int ex = x + xx - 1;
10315       int ey = y + yy - 1;
10316       int content_element = change->target_content.e[xx][yy];
10317       int e;
10318
10319       can_replace[xx][yy] = TRUE;
10320
10321       if (ex == x && ey == y)   /* do not check changing element itself */
10322         continue;
10323
10324       if (content_element == EL_EMPTY_SPACE)
10325       {
10326         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10327
10328         continue;
10329       }
10330
10331       if (!IN_LEV_FIELD(ex, ey))
10332       {
10333         can_replace[xx][yy] = FALSE;
10334         complete_replace = FALSE;
10335
10336         continue;
10337       }
10338
10339       e = Feld[ex][ey];
10340
10341       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10342         e = MovingOrBlocked2Element(ex, ey);
10343
10344       is_empty = (IS_FREE(ex, ey) ||
10345                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10346
10347       is_walkable     = (is_empty || IS_WALKABLE(e));
10348       is_diggable     = (is_empty || IS_DIGGABLE(e));
10349       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10350       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10351       is_removable    = (is_diggable || is_collectible);
10352
10353       can_replace[xx][yy] =
10354         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10355           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10356           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10357           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10358           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10359           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10360          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10361
10362       if (!can_replace[xx][yy])
10363         complete_replace = FALSE;
10364     }
10365
10366     if (!change->only_if_complete || complete_replace)
10367     {
10368       boolean something_has_changed = FALSE;
10369
10370       if (change->only_if_complete && change->use_random_replace &&
10371           RND(100) < change->random_percentage)
10372         return FALSE;
10373
10374       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10375       {
10376         int ex = x + xx - 1;
10377         int ey = y + yy - 1;
10378         int content_element;
10379
10380         if (can_replace[xx][yy] && (!change->use_random_replace ||
10381                                     RND(100) < change->random_percentage))
10382         {
10383           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10384             RemoveMovingField(ex, ey);
10385
10386           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10387
10388           content_element = change->target_content.e[xx][yy];
10389           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10390                                               ce_value, ce_score);
10391
10392           CreateElementFromChange(ex, ey, target_element);
10393
10394           something_has_changed = TRUE;
10395
10396           /* for symmetry reasons, freeze newly created border elements */
10397           if (ex != x || ey != y)
10398             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10399         }
10400       }
10401
10402       if (something_has_changed)
10403       {
10404         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10405         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10406       }
10407     }
10408   }
10409   else
10410   {
10411     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10412                                         ce_value, ce_score);
10413
10414     if (element == EL_DIAGONAL_GROWING ||
10415         element == EL_DIAGONAL_SHRINKING)
10416     {
10417       target_element = Store[x][y];
10418
10419       Store[x][y] = EL_EMPTY;
10420     }
10421
10422     CreateElementFromChange(x, y, target_element);
10423
10424     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10425     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10426   }
10427
10428   /* this uses direct change before indirect change */
10429   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10430
10431   return TRUE;
10432 }
10433
10434 static void HandleElementChange(int x, int y, int page)
10435 {
10436   int element = MovingOrBlocked2Element(x, y);
10437   struct ElementInfo *ei = &element_info[element];
10438   struct ElementChangeInfo *change = &ei->change_page[page];
10439   boolean handle_action_before_change = FALSE;
10440
10441 #ifdef DEBUG
10442   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10443       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10444   {
10445     printf("\n\n");
10446     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10447            x, y, element, element_info[element].token_name);
10448     printf("HandleElementChange(): This should never happen!\n");
10449     printf("\n\n");
10450   }
10451 #endif
10452
10453   /* this can happen with classic bombs on walkable, changing elements */
10454   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10455   {
10456     return;
10457   }
10458
10459   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10460   {
10461     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10462
10463     if (change->can_change)
10464     {
10465       /* !!! not clear why graphic animation should be reset at all here !!! */
10466       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10467       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10468
10469       /*
10470         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10471
10472         When using an animation frame delay of 1 (this only happens with
10473         "sp_zonk.moving.left/right" in the classic graphics), the default
10474         (non-moving) animation shows wrong animation frames (while the
10475         moving animation, like "sp_zonk.moving.left/right", is correct,
10476         so this graphical bug never shows up with the classic graphics).
10477         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10478         be drawn instead of the correct frames 0,1,2,3. This is caused by
10479         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10480         an element change: First when the change delay ("ChangeDelay[][]")
10481         counter has reached zero after decrementing, then a second time in
10482         the next frame (after "GfxFrame[][]" was already incremented) when
10483         "ChangeDelay[][]" is reset to the initial delay value again.
10484
10485         This causes frame 0 to be drawn twice, while the last frame won't
10486         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10487
10488         As some animations may already be cleverly designed around this bug
10489         (at least the "Snake Bite" snake tail animation does this), it cannot
10490         simply be fixed here without breaking such existing animations.
10491         Unfortunately, it cannot easily be detected if a graphics set was
10492         designed "before" or "after" the bug was fixed. As a workaround,
10493         a new graphics set option "game.graphics_engine_version" was added
10494         to be able to specify the game's major release version for which the
10495         graphics set was designed, which can then be used to decide if the
10496         bugfix should be used (version 4 and above) or not (version 3 or
10497         below, or if no version was specified at all, as with old sets).
10498
10499         (The wrong/fixed animation frames can be tested with the test level set
10500         "test_gfxframe" and level "000", which contains a specially prepared
10501         custom element at level position (x/y) == (11/9) which uses the zonk
10502         animation mentioned above. Using "game.graphics_engine_version: 4"
10503         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10504         This can also be seen from the debug output for this test element.)
10505       */
10506
10507       /* when a custom element is about to change (for example by change delay),
10508          do not reset graphic animation when the custom element is moving */
10509       if (game.graphics_engine_version < 4 &&
10510           !IS_MOVING(x, y))
10511       {
10512         ResetGfxAnimation(x, y);
10513         ResetRandomAnimationValue(x, y);
10514       }
10515
10516       if (change->pre_change_function)
10517         change->pre_change_function(x, y);
10518     }
10519   }
10520
10521   ChangeDelay[x][y]--;
10522
10523   if (ChangeDelay[x][y] != 0)           /* continue element change */
10524   {
10525     if (change->can_change)
10526     {
10527       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10528
10529       if (IS_ANIMATED(graphic))
10530         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10531
10532       if (change->change_function)
10533         change->change_function(x, y);
10534     }
10535   }
10536   else                                  /* finish element change */
10537   {
10538     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10539     {
10540       page = ChangePage[x][y];
10541       ChangePage[x][y] = -1;
10542
10543       change = &ei->change_page[page];
10544     }
10545
10546     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10547     {
10548       ChangeDelay[x][y] = 1;            /* try change after next move step */
10549       ChangePage[x][y] = page;          /* remember page to use for change */
10550
10551       return;
10552     }
10553
10554     /* special case: set new level random seed before changing element */
10555     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10556       handle_action_before_change = TRUE;
10557
10558     if (change->has_action && handle_action_before_change)
10559       ExecuteCustomElementAction(x, y, element, page);
10560
10561     if (change->can_change)
10562     {
10563       if (ChangeElement(x, y, element, page))
10564       {
10565         if (change->post_change_function)
10566           change->post_change_function(x, y);
10567       }
10568     }
10569
10570     if (change->has_action && !handle_action_before_change)
10571       ExecuteCustomElementAction(x, y, element, page);
10572   }
10573 }
10574
10575 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10576                                               int trigger_element,
10577                                               int trigger_event,
10578                                               int trigger_player,
10579                                               int trigger_side,
10580                                               int trigger_page)
10581 {
10582   boolean change_done_any = FALSE;
10583   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10584   int i;
10585
10586   if (!(trigger_events[trigger_element][trigger_event]))
10587     return FALSE;
10588
10589   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10590
10591   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10592   {
10593     int element = EL_CUSTOM_START + i;
10594     boolean change_done = FALSE;
10595     int p;
10596
10597     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10598         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10599       continue;
10600
10601     for (p = 0; p < element_info[element].num_change_pages; p++)
10602     {
10603       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10604
10605       if (change->can_change_or_has_action &&
10606           change->has_event[trigger_event] &&
10607           change->trigger_side & trigger_side &&
10608           change->trigger_player & trigger_player &&
10609           change->trigger_page & trigger_page_bits &&
10610           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10611       {
10612         change->actual_trigger_element = trigger_element;
10613         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10614         change->actual_trigger_player_bits = trigger_player;
10615         change->actual_trigger_side = trigger_side;
10616         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10617         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10618
10619         if ((change->can_change && !change_done) || change->has_action)
10620         {
10621           int x, y;
10622
10623           SCAN_PLAYFIELD(x, y)
10624           {
10625             if (Feld[x][y] == element)
10626             {
10627               if (change->can_change && !change_done)
10628               {
10629                 /* if element already changed in this frame, not only prevent
10630                    another element change (checked in ChangeElement()), but
10631                    also prevent additional element actions for this element */
10632
10633                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10634                     !level.use_action_after_change_bug)
10635                   continue;
10636
10637                 ChangeDelay[x][y] = 1;
10638                 ChangeEvent[x][y] = trigger_event;
10639
10640                 HandleElementChange(x, y, p);
10641               }
10642               else if (change->has_action)
10643               {
10644                 /* if element already changed in this frame, not only prevent
10645                    another element change (checked in ChangeElement()), but
10646                    also prevent additional element actions for this element */
10647
10648                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10649                     !level.use_action_after_change_bug)
10650                   continue;
10651
10652                 ExecuteCustomElementAction(x, y, element, p);
10653                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10654               }
10655             }
10656           }
10657
10658           if (change->can_change)
10659           {
10660             change_done = TRUE;
10661             change_done_any = TRUE;
10662           }
10663         }
10664       }
10665     }
10666   }
10667
10668   RECURSION_LOOP_DETECTION_END();
10669
10670   return change_done_any;
10671 }
10672
10673 static boolean CheckElementChangeExt(int x, int y,
10674                                      int element,
10675                                      int trigger_element,
10676                                      int trigger_event,
10677                                      int trigger_player,
10678                                      int trigger_side)
10679 {
10680   boolean change_done = FALSE;
10681   int p;
10682
10683   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10684       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10685     return FALSE;
10686
10687   if (Feld[x][y] == EL_BLOCKED)
10688   {
10689     Blocked2Moving(x, y, &x, &y);
10690     element = Feld[x][y];
10691   }
10692
10693   /* check if element has already changed or is about to change after moving */
10694   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10695        Feld[x][y] != element) ||
10696
10697       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10698        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10699         ChangePage[x][y] != -1)))
10700     return FALSE;
10701
10702   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10703
10704   for (p = 0; p < element_info[element].num_change_pages; p++)
10705   {
10706     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10707
10708     /* check trigger element for all events where the element that is checked
10709        for changing interacts with a directly adjacent element -- this is
10710        different to element changes that affect other elements to change on the
10711        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10712     boolean check_trigger_element =
10713       (trigger_event == CE_TOUCHING_X ||
10714        trigger_event == CE_HITTING_X ||
10715        trigger_event == CE_HIT_BY_X ||
10716        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10717
10718     if (change->can_change_or_has_action &&
10719         change->has_event[trigger_event] &&
10720         change->trigger_side & trigger_side &&
10721         change->trigger_player & trigger_player &&
10722         (!check_trigger_element ||
10723          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10724     {
10725       change->actual_trigger_element = trigger_element;
10726       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10727       change->actual_trigger_player_bits = trigger_player;
10728       change->actual_trigger_side = trigger_side;
10729       change->actual_trigger_ce_value = CustomValue[x][y];
10730       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10731
10732       /* special case: trigger element not at (x,y) position for some events */
10733       if (check_trigger_element)
10734       {
10735         static struct
10736         {
10737           int dx, dy;
10738         } move_xy[] =
10739           {
10740             {  0,  0 },
10741             { -1,  0 },
10742             { +1,  0 },
10743             {  0,  0 },
10744             {  0, -1 },
10745             {  0,  0 }, { 0, 0 }, { 0, 0 },
10746             {  0, +1 }
10747           };
10748
10749         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10750         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10751
10752         change->actual_trigger_ce_value = CustomValue[xx][yy];
10753         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10754       }
10755
10756       if (change->can_change && !change_done)
10757       {
10758         ChangeDelay[x][y] = 1;
10759         ChangeEvent[x][y] = trigger_event;
10760
10761         HandleElementChange(x, y, p);
10762
10763         change_done = TRUE;
10764       }
10765       else if (change->has_action)
10766       {
10767         ExecuteCustomElementAction(x, y, element, p);
10768         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10769       }
10770     }
10771   }
10772
10773   RECURSION_LOOP_DETECTION_END();
10774
10775   return change_done;
10776 }
10777
10778 static void PlayPlayerSound(struct PlayerInfo *player)
10779 {
10780   int jx = player->jx, jy = player->jy;
10781   int sound_element = player->artwork_element;
10782   int last_action = player->last_action_waiting;
10783   int action = player->action_waiting;
10784
10785   if (player->is_waiting)
10786   {
10787     if (action != last_action)
10788       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10789     else
10790       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10791   }
10792   else
10793   {
10794     if (action != last_action)
10795       StopSound(element_info[sound_element].sound[last_action]);
10796
10797     if (last_action == ACTION_SLEEPING)
10798       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10799   }
10800 }
10801
10802 static void PlayAllPlayersSound(void)
10803 {
10804   int i;
10805
10806   for (i = 0; i < MAX_PLAYERS; i++)
10807     if (stored_player[i].active)
10808       PlayPlayerSound(&stored_player[i]);
10809 }
10810
10811 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10812 {
10813   boolean last_waiting = player->is_waiting;
10814   int move_dir = player->MovDir;
10815
10816   player->dir_waiting = move_dir;
10817   player->last_action_waiting = player->action_waiting;
10818
10819   if (is_waiting)
10820   {
10821     if (!last_waiting)          /* not waiting -> waiting */
10822     {
10823       player->is_waiting = TRUE;
10824
10825       player->frame_counter_bored =
10826         FrameCounter +
10827         game.player_boring_delay_fixed +
10828         GetSimpleRandom(game.player_boring_delay_random);
10829       player->frame_counter_sleeping =
10830         FrameCounter +
10831         game.player_sleeping_delay_fixed +
10832         GetSimpleRandom(game.player_sleeping_delay_random);
10833
10834       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10835     }
10836
10837     if (game.player_sleeping_delay_fixed +
10838         game.player_sleeping_delay_random > 0 &&
10839         player->anim_delay_counter == 0 &&
10840         player->post_delay_counter == 0 &&
10841         FrameCounter >= player->frame_counter_sleeping)
10842       player->is_sleeping = TRUE;
10843     else if (game.player_boring_delay_fixed +
10844              game.player_boring_delay_random > 0 &&
10845              FrameCounter >= player->frame_counter_bored)
10846       player->is_bored = TRUE;
10847
10848     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10849                               player->is_bored ? ACTION_BORING :
10850                               ACTION_WAITING);
10851
10852     if (player->is_sleeping && player->use_murphy)
10853     {
10854       /* special case for sleeping Murphy when leaning against non-free tile */
10855
10856       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10857           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10858            !IS_MOVING(player->jx - 1, player->jy)))
10859         move_dir = MV_LEFT;
10860       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10861                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10862                 !IS_MOVING(player->jx + 1, player->jy)))
10863         move_dir = MV_RIGHT;
10864       else
10865         player->is_sleeping = FALSE;
10866
10867       player->dir_waiting = move_dir;
10868     }
10869
10870     if (player->is_sleeping)
10871     {
10872       if (player->num_special_action_sleeping > 0)
10873       {
10874         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10875         {
10876           int last_special_action = player->special_action_sleeping;
10877           int num_special_action = player->num_special_action_sleeping;
10878           int special_action =
10879             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10880              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10881              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10882              last_special_action + 1 : ACTION_SLEEPING);
10883           int special_graphic =
10884             el_act_dir2img(player->artwork_element, special_action, move_dir);
10885
10886           player->anim_delay_counter =
10887             graphic_info[special_graphic].anim_delay_fixed +
10888             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10889           player->post_delay_counter =
10890             graphic_info[special_graphic].post_delay_fixed +
10891             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10892
10893           player->special_action_sleeping = special_action;
10894         }
10895
10896         if (player->anim_delay_counter > 0)
10897         {
10898           player->action_waiting = player->special_action_sleeping;
10899           player->anim_delay_counter--;
10900         }
10901         else if (player->post_delay_counter > 0)
10902         {
10903           player->post_delay_counter--;
10904         }
10905       }
10906     }
10907     else if (player->is_bored)
10908     {
10909       if (player->num_special_action_bored > 0)
10910       {
10911         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10912         {
10913           int special_action =
10914             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10915           int special_graphic =
10916             el_act_dir2img(player->artwork_element, special_action, move_dir);
10917
10918           player->anim_delay_counter =
10919             graphic_info[special_graphic].anim_delay_fixed +
10920             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10921           player->post_delay_counter =
10922             graphic_info[special_graphic].post_delay_fixed +
10923             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10924
10925           player->special_action_bored = special_action;
10926         }
10927
10928         if (player->anim_delay_counter > 0)
10929         {
10930           player->action_waiting = player->special_action_bored;
10931           player->anim_delay_counter--;
10932         }
10933         else if (player->post_delay_counter > 0)
10934         {
10935           player->post_delay_counter--;
10936         }
10937       }
10938     }
10939   }
10940   else if (last_waiting)        /* waiting -> not waiting */
10941   {
10942     player->is_waiting = FALSE;
10943     player->is_bored = FALSE;
10944     player->is_sleeping = FALSE;
10945
10946     player->frame_counter_bored = -1;
10947     player->frame_counter_sleeping = -1;
10948
10949     player->anim_delay_counter = 0;
10950     player->post_delay_counter = 0;
10951
10952     player->dir_waiting = player->MovDir;
10953     player->action_waiting = ACTION_DEFAULT;
10954
10955     player->special_action_bored = ACTION_DEFAULT;
10956     player->special_action_sleeping = ACTION_DEFAULT;
10957   }
10958 }
10959
10960 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10961 {
10962   if ((!player->is_moving  && player->was_moving) ||
10963       (player->MovPos == 0 && player->was_moving) ||
10964       (player->is_snapping && !player->was_snapping) ||
10965       (player->is_dropping && !player->was_dropping))
10966   {
10967     if (!CheckSaveEngineSnapshotToList())
10968       return;
10969
10970     player->was_moving = FALSE;
10971     player->was_snapping = TRUE;
10972     player->was_dropping = TRUE;
10973   }
10974   else
10975   {
10976     if (player->is_moving)
10977       player->was_moving = TRUE;
10978
10979     if (!player->is_snapping)
10980       player->was_snapping = FALSE;
10981
10982     if (!player->is_dropping)
10983       player->was_dropping = FALSE;
10984   }
10985 }
10986
10987 static void CheckSingleStepMode(struct PlayerInfo *player)
10988 {
10989   if (tape.single_step && tape.recording && !tape.pausing)
10990   {
10991     /* as it is called "single step mode", just return to pause mode when the
10992        player stopped moving after one tile (or never starts moving at all) */
10993     if (!player->is_moving &&
10994         !player->is_pushing &&
10995         !player->is_dropping_pressed)
10996     {
10997       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10998       SnapField(player, 0, 0);                  /* stop snapping */
10999     }
11000   }
11001
11002   CheckSaveEngineSnapshot(player);
11003 }
11004
11005 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11006 {
11007   int left      = player_action & JOY_LEFT;
11008   int right     = player_action & JOY_RIGHT;
11009   int up        = player_action & JOY_UP;
11010   int down      = player_action & JOY_DOWN;
11011   int button1   = player_action & JOY_BUTTON_1;
11012   int button2   = player_action & JOY_BUTTON_2;
11013   int dx        = (left ? -1 : right ? 1 : 0);
11014   int dy        = (up   ? -1 : down  ? 1 : 0);
11015
11016   if (!player->active || tape.pausing)
11017     return 0;
11018
11019   if (player_action)
11020   {
11021     if (button1)
11022       SnapField(player, dx, dy);
11023     else
11024     {
11025       if (button2)
11026         DropElement(player);
11027
11028       MovePlayer(player, dx, dy);
11029     }
11030
11031     CheckSingleStepMode(player);
11032
11033     SetPlayerWaiting(player, FALSE);
11034
11035     return player_action;
11036   }
11037   else
11038   {
11039     /* no actions for this player (no input at player's configured device) */
11040
11041     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11042     SnapField(player, 0, 0);
11043     CheckGravityMovementWhenNotMoving(player);
11044
11045     if (player->MovPos == 0)
11046       SetPlayerWaiting(player, TRUE);
11047
11048     if (player->MovPos == 0)    /* needed for tape.playing */
11049       player->is_moving = FALSE;
11050
11051     player->is_dropping = FALSE;
11052     player->is_dropping_pressed = FALSE;
11053     player->drop_pressed_delay = 0;
11054
11055     CheckSingleStepMode(player);
11056
11057     return 0;
11058   }
11059 }
11060
11061 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11062                                          byte *tape_action)
11063 {
11064   if (!tape.use_mouse)
11065     return;
11066
11067   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11068   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11069   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11070 }
11071
11072 static void SetTapeActionFromMouseAction(byte *tape_action,
11073                                          struct MouseActionInfo *mouse_action)
11074 {
11075   if (!tape.use_mouse)
11076     return;
11077
11078   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11079   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11080   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11081 }
11082
11083 static void CheckLevelTime(void)
11084 {
11085   int i;
11086
11087   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11088   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11089   {
11090     if (level.native_em_level->lev->home == 0)  /* all players at home */
11091     {
11092       PlayerWins(local_player);
11093
11094       AllPlayersGone = TRUE;
11095
11096       level.native_em_level->lev->home = -1;
11097     }
11098
11099     if (level.native_em_level->ply[0]->alive == 0 &&
11100         level.native_em_level->ply[1]->alive == 0 &&
11101         level.native_em_level->ply[2]->alive == 0 &&
11102         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11103       AllPlayersGone = TRUE;
11104   }
11105   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11106   {
11107     if (game_sp.LevelSolved &&
11108         !game_sp.GameOver)                              /* game won */
11109     {
11110       PlayerWins(local_player);
11111
11112       game_sp.GameOver = TRUE;
11113
11114       AllPlayersGone = TRUE;
11115     }
11116
11117     if (game_sp.GameOver)                               /* game lost */
11118       AllPlayersGone = TRUE;
11119   }
11120   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11121   {
11122     if (game_mm.level_solved &&
11123         !game_mm.game_over)                             /* game won */
11124     {
11125       PlayerWins(local_player);
11126
11127       game_mm.game_over = TRUE;
11128
11129       AllPlayersGone = TRUE;
11130     }
11131
11132     if (game_mm.game_over)                              /* game lost */
11133       AllPlayersGone = TRUE;
11134   }
11135
11136   if (TimeFrames >= FRAMES_PER_SECOND)
11137   {
11138     TimeFrames = 0;
11139     TapeTime++;
11140
11141     for (i = 0; i < MAX_PLAYERS; i++)
11142     {
11143       struct PlayerInfo *player = &stored_player[i];
11144
11145       if (SHIELD_ON(player))
11146       {
11147         player->shield_normal_time_left--;
11148
11149         if (player->shield_deadly_time_left > 0)
11150           player->shield_deadly_time_left--;
11151       }
11152     }
11153
11154     if (!local_player->LevelSolved && !level.use_step_counter)
11155     {
11156       TimePlayed++;
11157
11158       if (TimeLeft > 0)
11159       {
11160         TimeLeft--;
11161
11162         if (TimeLeft <= 10 && setup.time_limit)
11163           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11164
11165         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11166            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11167
11168         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11169
11170         if (!TimeLeft && setup.time_limit)
11171         {
11172           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11173             level.native_em_level->lev->killed_out_of_time = TRUE;
11174           else
11175             for (i = 0; i < MAX_PLAYERS; i++)
11176               KillPlayer(&stored_player[i]);
11177         }
11178       }
11179       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11180       {
11181         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11182       }
11183
11184       level.native_em_level->lev->time =
11185         (game.no_time_limit ? TimePlayed : TimeLeft);
11186     }
11187
11188     if (tape.recording || tape.playing)
11189       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11190   }
11191
11192   if (tape.recording || tape.playing)
11193     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11194
11195   UpdateAndDisplayGameControlValues();
11196 }
11197
11198 void AdvanceFrameAndPlayerCounters(int player_nr)
11199 {
11200   int i;
11201
11202   /* advance frame counters (global frame counter and time frame counter) */
11203   FrameCounter++;
11204   TimeFrames++;
11205
11206   /* advance player counters (counters for move delay, move animation etc.) */
11207   for (i = 0; i < MAX_PLAYERS; i++)
11208   {
11209     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11210     int move_delay_value = stored_player[i].move_delay_value;
11211     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11212
11213     if (!advance_player_counters)       /* not all players may be affected */
11214       continue;
11215
11216     if (move_frames == 0)       /* less than one move per game frame */
11217     {
11218       int stepsize = TILEX / move_delay_value;
11219       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11220       int count = (stored_player[i].is_moving ?
11221                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11222
11223       if (count % delay == 0)
11224         move_frames = 1;
11225     }
11226
11227     stored_player[i].Frame += move_frames;
11228
11229     if (stored_player[i].MovPos != 0)
11230       stored_player[i].StepFrame += move_frames;
11231
11232     if (stored_player[i].move_delay > 0)
11233       stored_player[i].move_delay--;
11234
11235     /* due to bugs in previous versions, counter must count up, not down */
11236     if (stored_player[i].push_delay != -1)
11237       stored_player[i].push_delay++;
11238
11239     if (stored_player[i].drop_delay > 0)
11240       stored_player[i].drop_delay--;
11241
11242     if (stored_player[i].is_dropping_pressed)
11243       stored_player[i].drop_pressed_delay++;
11244   }
11245 }
11246
11247 void StartGameActions(boolean init_network_game, boolean record_tape,
11248                       int random_seed)
11249 {
11250   unsigned int new_random_seed = InitRND(random_seed);
11251
11252   if (record_tape)
11253     TapeStartRecording(new_random_seed);
11254
11255   if (init_network_game)
11256   {
11257     SendToServer_LevelFile();
11258     SendToServer_StartPlaying();
11259
11260     return;
11261   }
11262
11263   InitGame();
11264 }
11265
11266 static void GameActionsExt(void)
11267 {
11268 #if 0
11269   static unsigned int game_frame_delay = 0;
11270 #endif
11271   unsigned int game_frame_delay_value;
11272   byte *recorded_player_action;
11273   byte summarized_player_action = 0;
11274   byte tape_action[MAX_PLAYERS];
11275   int i;
11276
11277   /* detect endless loops, caused by custom element programming */
11278   if (recursion_loop_detected && recursion_loop_depth == 0)
11279   {
11280     char *message = getStringCat3("Internal Error! Element ",
11281                                   EL_NAME(recursion_loop_element),
11282                                   " caused endless loop! Quit the game?");
11283
11284     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11285           EL_NAME(recursion_loop_element));
11286
11287     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11288
11289     recursion_loop_detected = FALSE;    /* if game should be continued */
11290
11291     free(message);
11292
11293     return;
11294   }
11295
11296   if (game.restart_level)
11297     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11298
11299   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11300   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11301   {
11302     if (level.native_em_level->lev->home == 0)  /* all players at home */
11303     {
11304       PlayerWins(local_player);
11305
11306       AllPlayersGone = TRUE;
11307
11308       level.native_em_level->lev->home = -1;
11309     }
11310
11311     if (level.native_em_level->ply[0]->alive == 0 &&
11312         level.native_em_level->ply[1]->alive == 0 &&
11313         level.native_em_level->ply[2]->alive == 0 &&
11314         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11315       AllPlayersGone = TRUE;
11316   }
11317   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11318   {
11319     if (game_sp.LevelSolved &&
11320         !game_sp.GameOver)                              /* game won */
11321     {
11322       PlayerWins(local_player);
11323
11324       game_sp.GameOver = TRUE;
11325
11326       AllPlayersGone = TRUE;
11327     }
11328
11329     if (game_sp.GameOver)                               /* game lost */
11330       AllPlayersGone = TRUE;
11331   }
11332   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11333   {
11334     if (game_mm.level_solved &&
11335         !game_mm.game_over)                             /* game won */
11336     {
11337       PlayerWins(local_player);
11338
11339       game_mm.game_over = TRUE;
11340
11341       AllPlayersGone = TRUE;
11342     }
11343
11344     if (game_mm.game_over)                              /* game lost */
11345       AllPlayersGone = TRUE;
11346   }
11347
11348   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11349     GameWon();
11350
11351   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11352     TapeStop();
11353
11354   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11355     return;
11356
11357   game_frame_delay_value =
11358     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11359
11360   if (tape.playing && tape.warp_forward && !tape.pausing)
11361     game_frame_delay_value = 0;
11362
11363   SetVideoFrameDelay(game_frame_delay_value);
11364
11365 #if 0
11366 #if 0
11367   /* ---------- main game synchronization point ---------- */
11368
11369   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11370
11371   printf("::: skip == %d\n", skip);
11372
11373 #else
11374   /* ---------- main game synchronization point ---------- */
11375
11376   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11377 #endif
11378 #endif
11379
11380   if (network_playing && !network_player_action_received)
11381   {
11382     /* try to get network player actions in time */
11383
11384     /* last chance to get network player actions without main loop delay */
11385     HandleNetworking();
11386
11387     /* game was quit by network peer */
11388     if (game_status != GAME_MODE_PLAYING)
11389       return;
11390
11391     if (!network_player_action_received)
11392       return;           /* failed to get network player actions in time */
11393
11394     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11395   }
11396
11397   if (tape.pausing)
11398     return;
11399
11400   /* at this point we know that we really continue executing the game */
11401
11402   network_player_action_received = FALSE;
11403
11404   /* when playing tape, read previously recorded player input from tape data */
11405   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11406
11407   local_player->effective_mouse_action = local_player->mouse_action;
11408
11409   if (recorded_player_action != NULL)
11410     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11411                                  recorded_player_action);
11412
11413   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11414   if (tape.pausing)
11415     return;
11416
11417   if (tape.set_centered_player)
11418   {
11419     game.centered_player_nr_next = tape.centered_player_nr_next;
11420     game.set_centered_player = TRUE;
11421   }
11422
11423   for (i = 0; i < MAX_PLAYERS; i++)
11424   {
11425     summarized_player_action |= stored_player[i].action;
11426
11427     if (!network_playing && (game.team_mode || tape.playing))
11428       stored_player[i].effective_action = stored_player[i].action;
11429   }
11430
11431   if (network_playing)
11432     SendToServer_MovePlayer(summarized_player_action);
11433
11434   // summarize all actions at local players mapped input device position
11435   // (this allows using different input devices in single player mode)
11436   if (!network.enabled && !game.team_mode)
11437     stored_player[map_player_action[local_player->index_nr]].effective_action =
11438       summarized_player_action;
11439
11440   if (tape.recording &&
11441       setup.team_mode &&
11442       setup.input_on_focus &&
11443       game.centered_player_nr != -1)
11444   {
11445     for (i = 0; i < MAX_PLAYERS; i++)
11446       stored_player[i].effective_action =
11447         (i == game.centered_player_nr ? summarized_player_action : 0);
11448   }
11449
11450   if (recorded_player_action != NULL)
11451     for (i = 0; i < MAX_PLAYERS; i++)
11452       stored_player[i].effective_action = recorded_player_action[i];
11453
11454   for (i = 0; i < MAX_PLAYERS; i++)
11455   {
11456     tape_action[i] = stored_player[i].effective_action;
11457
11458     /* (this may happen in the RND game engine if a player was not present on
11459        the playfield on level start, but appeared later from a custom element */
11460     if (setup.team_mode &&
11461         tape.recording &&
11462         tape_action[i] &&
11463         !tape.player_participates[i])
11464       tape.player_participates[i] = TRUE;
11465   }
11466
11467   SetTapeActionFromMouseAction(tape_action,
11468                                &local_player->effective_mouse_action);
11469
11470   /* only record actions from input devices, but not programmed actions */
11471   if (tape.recording)
11472     TapeRecordAction(tape_action);
11473
11474 #if USE_NEW_PLAYER_ASSIGNMENTS
11475   // !!! also map player actions in single player mode !!!
11476   // if (game.team_mode)
11477   if (1)
11478   {
11479     byte mapped_action[MAX_PLAYERS];
11480
11481 #if DEBUG_PLAYER_ACTIONS
11482     printf(":::");
11483     for (i = 0; i < MAX_PLAYERS; i++)
11484       printf(" %d, ", stored_player[i].effective_action);
11485 #endif
11486
11487     for (i = 0; i < MAX_PLAYERS; i++)
11488       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11489
11490     for (i = 0; i < MAX_PLAYERS; i++)
11491       stored_player[i].effective_action = mapped_action[i];
11492
11493 #if DEBUG_PLAYER_ACTIONS
11494     printf(" =>");
11495     for (i = 0; i < MAX_PLAYERS; i++)
11496       printf(" %d, ", stored_player[i].effective_action);
11497     printf("\n");
11498 #endif
11499   }
11500 #if DEBUG_PLAYER_ACTIONS
11501   else
11502   {
11503     printf(":::");
11504     for (i = 0; i < MAX_PLAYERS; i++)
11505       printf(" %d, ", stored_player[i].effective_action);
11506     printf("\n");
11507   }
11508 #endif
11509 #endif
11510
11511   for (i = 0; i < MAX_PLAYERS; i++)
11512   {
11513     // allow engine snapshot in case of changed movement attempt
11514     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11515         (stored_player[i].effective_action & KEY_MOTION))
11516       game.snapshot.changed_action = TRUE;
11517
11518     // allow engine snapshot in case of snapping/dropping attempt
11519     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11520         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11521       game.snapshot.changed_action = TRUE;
11522
11523     game.snapshot.last_action[i] = stored_player[i].effective_action;
11524   }
11525
11526   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11527   {
11528     GameActions_EM_Main();
11529   }
11530   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11531   {
11532     GameActions_SP_Main();
11533   }
11534   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11535   {
11536     GameActions_MM_Main();
11537   }
11538   else
11539   {
11540     GameActions_RND_Main();
11541   }
11542
11543   BlitScreenToBitmap(backbuffer);
11544
11545   CheckLevelTime();
11546
11547   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11548
11549   if (global.show_frames_per_second)
11550   {
11551     static unsigned int fps_counter = 0;
11552     static int fps_frames = 0;
11553     unsigned int fps_delay_ms = Counter() - fps_counter;
11554
11555     fps_frames++;
11556
11557     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11558     {
11559       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11560
11561       fps_frames = 0;
11562       fps_counter = Counter();
11563
11564       /* always draw FPS to screen after FPS value was updated */
11565       redraw_mask |= REDRAW_FPS;
11566     }
11567
11568     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11569     if (GetDrawDeactivationMask() == REDRAW_NONE)
11570       redraw_mask |= REDRAW_FPS;
11571   }
11572 }
11573
11574 static void GameActions_CheckSaveEngineSnapshot(void)
11575 {
11576   if (!game.snapshot.save_snapshot)
11577     return;
11578
11579   // clear flag for saving snapshot _before_ saving snapshot
11580   game.snapshot.save_snapshot = FALSE;
11581
11582   SaveEngineSnapshotToList();
11583 }
11584
11585 void GameActions(void)
11586 {
11587   GameActionsExt();
11588
11589   GameActions_CheckSaveEngineSnapshot();
11590 }
11591
11592 void GameActions_EM_Main(void)
11593 {
11594   byte effective_action[MAX_PLAYERS];
11595   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11596   int i;
11597
11598   for (i = 0; i < MAX_PLAYERS; i++)
11599     effective_action[i] = stored_player[i].effective_action;
11600
11601   GameActions_EM(effective_action, warp_mode);
11602 }
11603
11604 void GameActions_SP_Main(void)
11605 {
11606   byte effective_action[MAX_PLAYERS];
11607   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11608   int i;
11609
11610   for (i = 0; i < MAX_PLAYERS; i++)
11611     effective_action[i] = stored_player[i].effective_action;
11612
11613   GameActions_SP(effective_action, warp_mode);
11614
11615   for (i = 0; i < MAX_PLAYERS; i++)
11616   {
11617     if (stored_player[i].force_dropping)
11618       stored_player[i].action |= KEY_BUTTON_DROP;
11619
11620     stored_player[i].force_dropping = FALSE;
11621   }
11622 }
11623
11624 void GameActions_MM_Main(void)
11625 {
11626   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627
11628   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11629 }
11630
11631 void GameActions_RND_Main(void)
11632 {
11633   GameActions_RND();
11634 }
11635
11636 void GameActions_RND(void)
11637 {
11638   int magic_wall_x = 0, magic_wall_y = 0;
11639   int i, x, y, element, graphic, last_gfx_frame;
11640
11641   InitPlayfieldScanModeVars();
11642
11643   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11644   {
11645     SCAN_PLAYFIELD(x, y)
11646     {
11647       ChangeCount[x][y] = 0;
11648       ChangeEvent[x][y] = -1;
11649     }
11650   }
11651
11652   if (game.set_centered_player)
11653   {
11654     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11655
11656     /* switching to "all players" only possible if all players fit to screen */
11657     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11658     {
11659       game.centered_player_nr_next = game.centered_player_nr;
11660       game.set_centered_player = FALSE;
11661     }
11662
11663     /* do not switch focus to non-existing (or non-active) player */
11664     if (game.centered_player_nr_next >= 0 &&
11665         !stored_player[game.centered_player_nr_next].active)
11666     {
11667       game.centered_player_nr_next = game.centered_player_nr;
11668       game.set_centered_player = FALSE;
11669     }
11670   }
11671
11672   if (game.set_centered_player &&
11673       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11674   {
11675     int sx, sy;
11676
11677     if (game.centered_player_nr_next == -1)
11678     {
11679       setScreenCenteredToAllPlayers(&sx, &sy);
11680     }
11681     else
11682     {
11683       sx = stored_player[game.centered_player_nr_next].jx;
11684       sy = stored_player[game.centered_player_nr_next].jy;
11685     }
11686
11687     game.centered_player_nr = game.centered_player_nr_next;
11688     game.set_centered_player = FALSE;
11689
11690     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11691     DrawGameDoorValues();
11692   }
11693
11694   for (i = 0; i < MAX_PLAYERS; i++)
11695   {
11696     int actual_player_action = stored_player[i].effective_action;
11697
11698 #if 1
11699     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11700        - rnd_equinox_tetrachloride 048
11701        - rnd_equinox_tetrachloride_ii 096
11702        - rnd_emanuel_schmieg 002
11703        - doctor_sloan_ww 001, 020
11704     */
11705     if (stored_player[i].MovPos == 0)
11706       CheckGravityMovement(&stored_player[i]);
11707 #endif
11708
11709     /* overwrite programmed action with tape action */
11710     if (stored_player[i].programmed_action)
11711       actual_player_action = stored_player[i].programmed_action;
11712
11713     PlayerActions(&stored_player[i], actual_player_action);
11714
11715     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11716   }
11717
11718   ScrollScreen(NULL, SCROLL_GO_ON);
11719
11720   /* for backwards compatibility, the following code emulates a fixed bug that
11721      occured when pushing elements (causing elements that just made their last
11722      pushing step to already (if possible) make their first falling step in the
11723      same game frame, which is bad); this code is also needed to use the famous
11724      "spring push bug" which is used in older levels and might be wanted to be
11725      used also in newer levels, but in this case the buggy pushing code is only
11726      affecting the "spring" element and no other elements */
11727
11728   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11729   {
11730     for (i = 0; i < MAX_PLAYERS; i++)
11731     {
11732       struct PlayerInfo *player = &stored_player[i];
11733       int x = player->jx;
11734       int y = player->jy;
11735
11736       if (player->active && player->is_pushing && player->is_moving &&
11737           IS_MOVING(x, y) &&
11738           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11739            Feld[x][y] == EL_SPRING))
11740       {
11741         ContinueMoving(x, y);
11742
11743         /* continue moving after pushing (this is actually a bug) */
11744         if (!IS_MOVING(x, y))
11745           Stop[x][y] = FALSE;
11746       }
11747     }
11748   }
11749
11750   SCAN_PLAYFIELD(x, y)
11751   {
11752     Last[x][y] = Feld[x][y];
11753
11754     ChangeCount[x][y] = 0;
11755     ChangeEvent[x][y] = -1;
11756
11757     /* this must be handled before main playfield loop */
11758     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11759     {
11760       MovDelay[x][y]--;
11761       if (MovDelay[x][y] <= 0)
11762         RemoveField(x, y);
11763     }
11764
11765     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11766     {
11767       MovDelay[x][y]--;
11768       if (MovDelay[x][y] <= 0)
11769       {
11770         RemoveField(x, y);
11771         TEST_DrawLevelField(x, y);
11772
11773         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11774       }
11775     }
11776
11777 #if DEBUG
11778     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11779     {
11780       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11781       printf("GameActions(): This should never happen!\n");
11782
11783       ChangePage[x][y] = -1;
11784     }
11785 #endif
11786
11787     Stop[x][y] = FALSE;
11788     if (WasJustMoving[x][y] > 0)
11789       WasJustMoving[x][y]--;
11790     if (WasJustFalling[x][y] > 0)
11791       WasJustFalling[x][y]--;
11792     if (CheckCollision[x][y] > 0)
11793       CheckCollision[x][y]--;
11794     if (CheckImpact[x][y] > 0)
11795       CheckImpact[x][y]--;
11796
11797     GfxFrame[x][y]++;
11798
11799     /* reset finished pushing action (not done in ContinueMoving() to allow
11800        continuous pushing animation for elements with zero push delay) */
11801     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11802     {
11803       ResetGfxAnimation(x, y);
11804       TEST_DrawLevelField(x, y);
11805     }
11806
11807 #if DEBUG
11808     if (IS_BLOCKED(x, y))
11809     {
11810       int oldx, oldy;
11811
11812       Blocked2Moving(x, y, &oldx, &oldy);
11813       if (!IS_MOVING(oldx, oldy))
11814       {
11815         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11816         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11817         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11818         printf("GameActions(): This should never happen!\n");
11819       }
11820     }
11821 #endif
11822   }
11823
11824   SCAN_PLAYFIELD(x, y)
11825   {
11826     element = Feld[x][y];
11827     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11828     last_gfx_frame = GfxFrame[x][y];
11829
11830     ResetGfxFrame(x, y);
11831
11832     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11833       DrawLevelGraphicAnimation(x, y, graphic);
11834
11835     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11836         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11837       ResetRandomAnimationValue(x, y);
11838
11839     SetRandomAnimationValue(x, y);
11840
11841     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11842
11843     if (IS_INACTIVE(element))
11844     {
11845       if (IS_ANIMATED(graphic))
11846         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11847
11848       continue;
11849     }
11850
11851     /* this may take place after moving, so 'element' may have changed */
11852     if (IS_CHANGING(x, y) &&
11853         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11854     {
11855       int page = element_info[element].event_page_nr[CE_DELAY];
11856
11857       HandleElementChange(x, y, page);
11858
11859       element = Feld[x][y];
11860       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11861     }
11862
11863     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11864     {
11865       StartMoving(x, y);
11866
11867       element = Feld[x][y];
11868       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11869
11870       if (IS_ANIMATED(graphic) &&
11871           !IS_MOVING(x, y) &&
11872           !Stop[x][y])
11873         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11874
11875       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11876         TEST_DrawTwinkleOnField(x, y);
11877     }
11878     else if (element == EL_ACID)
11879     {
11880       if (!Stop[x][y])
11881         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11882     }
11883     else if ((element == EL_EXIT_OPEN ||
11884               element == EL_EM_EXIT_OPEN ||
11885               element == EL_SP_EXIT_OPEN ||
11886               element == EL_STEEL_EXIT_OPEN ||
11887               element == EL_EM_STEEL_EXIT_OPEN ||
11888               element == EL_SP_TERMINAL ||
11889               element == EL_SP_TERMINAL_ACTIVE ||
11890               element == EL_EXTRA_TIME ||
11891               element == EL_SHIELD_NORMAL ||
11892               element == EL_SHIELD_DEADLY) &&
11893              IS_ANIMATED(graphic))
11894       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11895     else if (IS_MOVING(x, y))
11896       ContinueMoving(x, y);
11897     else if (IS_ACTIVE_BOMB(element))
11898       CheckDynamite(x, y);
11899     else if (element == EL_AMOEBA_GROWING)
11900       AmoebeWaechst(x, y);
11901     else if (element == EL_AMOEBA_SHRINKING)
11902       AmoebaDisappearing(x, y);
11903
11904 #if !USE_NEW_AMOEBA_CODE
11905     else if (IS_AMOEBALIVE(element))
11906       AmoebeAbleger(x, y);
11907 #endif
11908
11909     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11910       Life(x, y);
11911     else if (element == EL_EXIT_CLOSED)
11912       CheckExit(x, y);
11913     else if (element == EL_EM_EXIT_CLOSED)
11914       CheckExitEM(x, y);
11915     else if (element == EL_STEEL_EXIT_CLOSED)
11916       CheckExitSteel(x, y);
11917     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11918       CheckExitSteelEM(x, y);
11919     else if (element == EL_SP_EXIT_CLOSED)
11920       CheckExitSP(x, y);
11921     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11922              element == EL_EXPANDABLE_STEELWALL_GROWING)
11923       MauerWaechst(x, y);
11924     else if (element == EL_EXPANDABLE_WALL ||
11925              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11926              element == EL_EXPANDABLE_WALL_VERTICAL ||
11927              element == EL_EXPANDABLE_WALL_ANY ||
11928              element == EL_BD_EXPANDABLE_WALL)
11929       MauerAbleger(x, y);
11930     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11931              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11932              element == EL_EXPANDABLE_STEELWALL_ANY)
11933       MauerAblegerStahl(x, y);
11934     else if (element == EL_FLAMES)
11935       CheckForDragon(x, y);
11936     else if (element == EL_EXPLOSION)
11937       ; /* drawing of correct explosion animation is handled separately */
11938     else if (element == EL_ELEMENT_SNAPPING ||
11939              element == EL_DIAGONAL_SHRINKING ||
11940              element == EL_DIAGONAL_GROWING)
11941     {
11942       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11943
11944       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11945     }
11946     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11947       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11948
11949     if (IS_BELT_ACTIVE(element))
11950       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11951
11952     if (game.magic_wall_active)
11953     {
11954       int jx = local_player->jx, jy = local_player->jy;
11955
11956       /* play the element sound at the position nearest to the player */
11957       if ((element == EL_MAGIC_WALL_FULL ||
11958            element == EL_MAGIC_WALL_ACTIVE ||
11959            element == EL_MAGIC_WALL_EMPTYING ||
11960            element == EL_BD_MAGIC_WALL_FULL ||
11961            element == EL_BD_MAGIC_WALL_ACTIVE ||
11962            element == EL_BD_MAGIC_WALL_EMPTYING ||
11963            element == EL_DC_MAGIC_WALL_FULL ||
11964            element == EL_DC_MAGIC_WALL_ACTIVE ||
11965            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11966           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11967       {
11968         magic_wall_x = x;
11969         magic_wall_y = y;
11970       }
11971     }
11972   }
11973
11974 #if USE_NEW_AMOEBA_CODE
11975   /* new experimental amoeba growth stuff */
11976   if (!(FrameCounter % 8))
11977   {
11978     static unsigned int random = 1684108901;
11979
11980     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11981     {
11982       x = RND(lev_fieldx);
11983       y = RND(lev_fieldy);
11984       element = Feld[x][y];
11985
11986       if (!IS_PLAYER(x,y) &&
11987           (element == EL_EMPTY ||
11988            CAN_GROW_INTO(element) ||
11989            element == EL_QUICKSAND_EMPTY ||
11990            element == EL_QUICKSAND_FAST_EMPTY ||
11991            element == EL_ACID_SPLASH_LEFT ||
11992            element == EL_ACID_SPLASH_RIGHT))
11993       {
11994         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11995             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11996             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11997             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11998           Feld[x][y] = EL_AMOEBA_DROP;
11999       }
12000
12001       random = random * 129 + 1;
12002     }
12003   }
12004 #endif
12005
12006   game.explosions_delayed = FALSE;
12007
12008   SCAN_PLAYFIELD(x, y)
12009   {
12010     element = Feld[x][y];
12011
12012     if (ExplodeField[x][y])
12013       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12014     else if (element == EL_EXPLOSION)
12015       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12016
12017     ExplodeField[x][y] = EX_TYPE_NONE;
12018   }
12019
12020   game.explosions_delayed = TRUE;
12021
12022   if (game.magic_wall_active)
12023   {
12024     if (!(game.magic_wall_time_left % 4))
12025     {
12026       int element = Feld[magic_wall_x][magic_wall_y];
12027
12028       if (element == EL_BD_MAGIC_WALL_FULL ||
12029           element == EL_BD_MAGIC_WALL_ACTIVE ||
12030           element == EL_BD_MAGIC_WALL_EMPTYING)
12031         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12032       else if (element == EL_DC_MAGIC_WALL_FULL ||
12033                element == EL_DC_MAGIC_WALL_ACTIVE ||
12034                element == EL_DC_MAGIC_WALL_EMPTYING)
12035         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12036       else
12037         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12038     }
12039
12040     if (game.magic_wall_time_left > 0)
12041     {
12042       game.magic_wall_time_left--;
12043
12044       if (!game.magic_wall_time_left)
12045       {
12046         SCAN_PLAYFIELD(x, y)
12047         {
12048           element = Feld[x][y];
12049
12050           if (element == EL_MAGIC_WALL_ACTIVE ||
12051               element == EL_MAGIC_WALL_FULL)
12052           {
12053             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12054             TEST_DrawLevelField(x, y);
12055           }
12056           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12057                    element == EL_BD_MAGIC_WALL_FULL)
12058           {
12059             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12060             TEST_DrawLevelField(x, y);
12061           }
12062           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12063                    element == EL_DC_MAGIC_WALL_FULL)
12064           {
12065             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12066             TEST_DrawLevelField(x, y);
12067           }
12068         }
12069
12070         game.magic_wall_active = FALSE;
12071       }
12072     }
12073   }
12074
12075   if (game.light_time_left > 0)
12076   {
12077     game.light_time_left--;
12078
12079     if (game.light_time_left == 0)
12080       RedrawAllLightSwitchesAndInvisibleElements();
12081   }
12082
12083   if (game.timegate_time_left > 0)
12084   {
12085     game.timegate_time_left--;
12086
12087     if (game.timegate_time_left == 0)
12088       CloseAllOpenTimegates();
12089   }
12090
12091   if (game.lenses_time_left > 0)
12092   {
12093     game.lenses_time_left--;
12094
12095     if (game.lenses_time_left == 0)
12096       RedrawAllInvisibleElementsForLenses();
12097   }
12098
12099   if (game.magnify_time_left > 0)
12100   {
12101     game.magnify_time_left--;
12102
12103     if (game.magnify_time_left == 0)
12104       RedrawAllInvisibleElementsForMagnifier();
12105   }
12106
12107   for (i = 0; i < MAX_PLAYERS; i++)
12108   {
12109     struct PlayerInfo *player = &stored_player[i];
12110
12111     if (SHIELD_ON(player))
12112     {
12113       if (player->shield_deadly_time_left)
12114         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12115       else if (player->shield_normal_time_left)
12116         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12117     }
12118   }
12119
12120 #if USE_DELAYED_GFX_REDRAW
12121   SCAN_PLAYFIELD(x, y)
12122   {
12123     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12124     {
12125       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12126          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12127
12128       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12129         DrawLevelField(x, y);
12130
12131       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12132         DrawLevelFieldCrumbled(x, y);
12133
12134       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12135         DrawLevelFieldCrumbledNeighbours(x, y);
12136
12137       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12138         DrawTwinkleOnField(x, y);
12139     }
12140
12141     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12142   }
12143 #endif
12144
12145   DrawAllPlayers();
12146   PlayAllPlayersSound();
12147
12148   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12149   {
12150     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12151
12152     local_player->show_envelope = 0;
12153   }
12154
12155   /* use random number generator in every frame to make it less predictable */
12156   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12157     RND(1);
12158 }
12159
12160 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12161 {
12162   int min_x = x, min_y = y, max_x = x, max_y = y;
12163   int i;
12164
12165   for (i = 0; i < MAX_PLAYERS; i++)
12166   {
12167     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12168
12169     if (!stored_player[i].active || &stored_player[i] == player)
12170       continue;
12171
12172     min_x = MIN(min_x, jx);
12173     min_y = MIN(min_y, jy);
12174     max_x = MAX(max_x, jx);
12175     max_y = MAX(max_y, jy);
12176   }
12177
12178   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12179 }
12180
12181 static boolean AllPlayersInVisibleScreen(void)
12182 {
12183   int i;
12184
12185   for (i = 0; i < MAX_PLAYERS; i++)
12186   {
12187     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12188
12189     if (!stored_player[i].active)
12190       continue;
12191
12192     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12193       return FALSE;
12194   }
12195
12196   return TRUE;
12197 }
12198
12199 void ScrollLevel(int dx, int dy)
12200 {
12201   int scroll_offset = 2 * TILEX_VAR;
12202   int x, y;
12203
12204   BlitBitmap(drawto_field, drawto_field,
12205              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12206              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12207              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12208              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12209              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12210              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12211
12212   if (dx != 0)
12213   {
12214     x = (dx == 1 ? BX1 : BX2);
12215     for (y = BY1; y <= BY2; y++)
12216       DrawScreenField(x, y);
12217   }
12218
12219   if (dy != 0)
12220   {
12221     y = (dy == 1 ? BY1 : BY2);
12222     for (x = BX1; x <= BX2; x++)
12223       DrawScreenField(x, y);
12224   }
12225
12226   redraw_mask |= REDRAW_FIELD;
12227 }
12228
12229 static boolean canFallDown(struct PlayerInfo *player)
12230 {
12231   int jx = player->jx, jy = player->jy;
12232
12233   return (IN_LEV_FIELD(jx, jy + 1) &&
12234           (IS_FREE(jx, jy + 1) ||
12235            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12236           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12237           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12238 }
12239
12240 static boolean canPassField(int x, int y, int move_dir)
12241 {
12242   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12243   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12244   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12245   int nextx = x + dx;
12246   int nexty = y + dy;
12247   int element = Feld[x][y];
12248
12249   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12250           !CAN_MOVE(element) &&
12251           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12252           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12253           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12254 }
12255
12256 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12257 {
12258   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12259   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12260   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12261   int newx = x + dx;
12262   int newy = y + dy;
12263
12264   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12265           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12266           (IS_DIGGABLE(Feld[newx][newy]) ||
12267            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12268            canPassField(newx, newy, move_dir)));
12269 }
12270
12271 static void CheckGravityMovement(struct PlayerInfo *player)
12272 {
12273   if (player->gravity && !player->programmed_action)
12274   {
12275     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12276     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12277     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12278     int jx = player->jx, jy = player->jy;
12279     boolean player_is_moving_to_valid_field =
12280       (!player_is_snapping &&
12281        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12282         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12283     boolean player_can_fall_down = canFallDown(player);
12284
12285     if (player_can_fall_down &&
12286         !player_is_moving_to_valid_field)
12287       player->programmed_action = MV_DOWN;
12288   }
12289 }
12290
12291 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12292 {
12293   return CheckGravityMovement(player);
12294
12295   if (player->gravity && !player->programmed_action)
12296   {
12297     int jx = player->jx, jy = player->jy;
12298     boolean field_under_player_is_free =
12299       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12300     boolean player_is_standing_on_valid_field =
12301       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12302        (IS_WALKABLE(Feld[jx][jy]) &&
12303         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12304
12305     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12306       player->programmed_action = MV_DOWN;
12307   }
12308 }
12309
12310 /*
12311   MovePlayerOneStep()
12312   -----------------------------------------------------------------------------
12313   dx, dy:               direction (non-diagonal) to try to move the player to
12314   real_dx, real_dy:     direction as read from input device (can be diagonal)
12315 */
12316
12317 boolean MovePlayerOneStep(struct PlayerInfo *player,
12318                           int dx, int dy, int real_dx, int real_dy)
12319 {
12320   int jx = player->jx, jy = player->jy;
12321   int new_jx = jx + dx, new_jy = jy + dy;
12322   int can_move;
12323   boolean player_can_move = !player->cannot_move;
12324
12325   if (!player->active || (!dx && !dy))
12326     return MP_NO_ACTION;
12327
12328   player->MovDir = (dx < 0 ? MV_LEFT :
12329                     dx > 0 ? MV_RIGHT :
12330                     dy < 0 ? MV_UP :
12331                     dy > 0 ? MV_DOWN :  MV_NONE);
12332
12333   if (!IN_LEV_FIELD(new_jx, new_jy))
12334     return MP_NO_ACTION;
12335
12336   if (!player_can_move)
12337   {
12338     if (player->MovPos == 0)
12339     {
12340       player->is_moving = FALSE;
12341       player->is_digging = FALSE;
12342       player->is_collecting = FALSE;
12343       player->is_snapping = FALSE;
12344       player->is_pushing = FALSE;
12345     }
12346   }
12347
12348   if (!network.enabled && game.centered_player_nr == -1 &&
12349       !AllPlayersInSight(player, new_jx, new_jy))
12350     return MP_NO_ACTION;
12351
12352   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12353   if (can_move != MP_MOVING)
12354     return can_move;
12355
12356   /* check if DigField() has caused relocation of the player */
12357   if (player->jx != jx || player->jy != jy)
12358     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12359
12360   StorePlayer[jx][jy] = 0;
12361   player->last_jx = jx;
12362   player->last_jy = jy;
12363   player->jx = new_jx;
12364   player->jy = new_jy;
12365   StorePlayer[new_jx][new_jy] = player->element_nr;
12366
12367   if (player->move_delay_value_next != -1)
12368   {
12369     player->move_delay_value = player->move_delay_value_next;
12370     player->move_delay_value_next = -1;
12371   }
12372
12373   player->MovPos =
12374     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12375
12376   player->step_counter++;
12377
12378   PlayerVisit[jx][jy] = FrameCounter;
12379
12380   player->is_moving = TRUE;
12381
12382 #if 1
12383   /* should better be called in MovePlayer(), but this breaks some tapes */
12384   ScrollPlayer(player, SCROLL_INIT);
12385 #endif
12386
12387   return MP_MOVING;
12388 }
12389
12390 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12391 {
12392   int jx = player->jx, jy = player->jy;
12393   int old_jx = jx, old_jy = jy;
12394   int moved = MP_NO_ACTION;
12395
12396   if (!player->active)
12397     return FALSE;
12398
12399   if (!dx && !dy)
12400   {
12401     if (player->MovPos == 0)
12402     {
12403       player->is_moving = FALSE;
12404       player->is_digging = FALSE;
12405       player->is_collecting = FALSE;
12406       player->is_snapping = FALSE;
12407       player->is_pushing = FALSE;
12408     }
12409
12410     return FALSE;
12411   }
12412
12413   if (player->move_delay > 0)
12414     return FALSE;
12415
12416   player->move_delay = -1;              /* set to "uninitialized" value */
12417
12418   /* store if player is automatically moved to next field */
12419   player->is_auto_moving = (player->programmed_action != MV_NONE);
12420
12421   /* remove the last programmed player action */
12422   player->programmed_action = 0;
12423
12424   if (player->MovPos)
12425   {
12426     /* should only happen if pre-1.2 tape recordings are played */
12427     /* this is only for backward compatibility */
12428
12429     int original_move_delay_value = player->move_delay_value;
12430
12431 #if DEBUG
12432     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12433            tape.counter);
12434 #endif
12435
12436     /* scroll remaining steps with finest movement resolution */
12437     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12438
12439     while (player->MovPos)
12440     {
12441       ScrollPlayer(player, SCROLL_GO_ON);
12442       ScrollScreen(NULL, SCROLL_GO_ON);
12443
12444       AdvanceFrameAndPlayerCounters(player->index_nr);
12445
12446       DrawAllPlayers();
12447       BackToFront_WithFrameDelay(0);
12448     }
12449
12450     player->move_delay_value = original_move_delay_value;
12451   }
12452
12453   player->is_active = FALSE;
12454
12455   if (player->last_move_dir & MV_HORIZONTAL)
12456   {
12457     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12458       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12459   }
12460   else
12461   {
12462     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12463       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12464   }
12465
12466   if (!moved && !player->is_active)
12467   {
12468     player->is_moving = FALSE;
12469     player->is_digging = FALSE;
12470     player->is_collecting = FALSE;
12471     player->is_snapping = FALSE;
12472     player->is_pushing = FALSE;
12473   }
12474
12475   jx = player->jx;
12476   jy = player->jy;
12477
12478   if (moved & MP_MOVING && !ScreenMovPos &&
12479       (player->index_nr == game.centered_player_nr ||
12480        game.centered_player_nr == -1))
12481   {
12482     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12483     int offset = game.scroll_delay_value;
12484
12485     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12486     {
12487       /* actual player has left the screen -- scroll in that direction */
12488       if (jx != old_jx)         /* player has moved horizontally */
12489         scroll_x += (jx - old_jx);
12490       else                      /* player has moved vertically */
12491         scroll_y += (jy - old_jy);
12492     }
12493     else
12494     {
12495       if (jx != old_jx)         /* player has moved horizontally */
12496       {
12497         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12498             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12499           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12500
12501         /* don't scroll over playfield boundaries */
12502         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12503           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12504
12505         /* don't scroll more than one field at a time */
12506         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12507
12508         /* don't scroll against the player's moving direction */
12509         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12510             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12511           scroll_x = old_scroll_x;
12512       }
12513       else                      /* player has moved vertically */
12514       {
12515         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12516             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12517           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12518
12519         /* don't scroll over playfield boundaries */
12520         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12521           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12522
12523         /* don't scroll more than one field at a time */
12524         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12525
12526         /* don't scroll against the player's moving direction */
12527         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12528             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12529           scroll_y = old_scroll_y;
12530       }
12531     }
12532
12533     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12534     {
12535       if (!network.enabled && game.centered_player_nr == -1 &&
12536           !AllPlayersInVisibleScreen())
12537       {
12538         scroll_x = old_scroll_x;
12539         scroll_y = old_scroll_y;
12540       }
12541       else
12542       {
12543         ScrollScreen(player, SCROLL_INIT);
12544         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12545       }
12546     }
12547   }
12548
12549   player->StepFrame = 0;
12550
12551   if (moved & MP_MOVING)
12552   {
12553     if (old_jx != jx && old_jy == jy)
12554       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12555     else if (old_jx == jx && old_jy != jy)
12556       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12557
12558     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12559
12560     player->last_move_dir = player->MovDir;
12561     player->is_moving = TRUE;
12562     player->is_snapping = FALSE;
12563     player->is_switching = FALSE;
12564     player->is_dropping = FALSE;
12565     player->is_dropping_pressed = FALSE;
12566     player->drop_pressed_delay = 0;
12567
12568 #if 0
12569     /* should better be called here than above, but this breaks some tapes */
12570     ScrollPlayer(player, SCROLL_INIT);
12571 #endif
12572   }
12573   else
12574   {
12575     CheckGravityMovementWhenNotMoving(player);
12576
12577     player->is_moving = FALSE;
12578
12579     /* at this point, the player is allowed to move, but cannot move right now
12580        (e.g. because of something blocking the way) -- ensure that the player
12581        is also allowed to move in the next frame (in old versions before 3.1.1,
12582        the player was forced to wait again for eight frames before next try) */
12583
12584     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12585       player->move_delay = 0;   /* allow direct movement in the next frame */
12586   }
12587
12588   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12589     player->move_delay = player->move_delay_value;
12590
12591   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12592   {
12593     TestIfPlayerTouchesBadThing(jx, jy);
12594     TestIfPlayerTouchesCustomElement(jx, jy);
12595   }
12596
12597   if (!player->active)
12598     RemovePlayer(player);
12599
12600   return moved;
12601 }
12602
12603 void ScrollPlayer(struct PlayerInfo *player, int mode)
12604 {
12605   int jx = player->jx, jy = player->jy;
12606   int last_jx = player->last_jx, last_jy = player->last_jy;
12607   int move_stepsize = TILEX / player->move_delay_value;
12608
12609   if (!player->active)
12610     return;
12611
12612   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12613     return;
12614
12615   if (mode == SCROLL_INIT)
12616   {
12617     player->actual_frame_counter = FrameCounter;
12618     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12619
12620     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12621         Feld[last_jx][last_jy] == EL_EMPTY)
12622     {
12623       int last_field_block_delay = 0;   /* start with no blocking at all */
12624       int block_delay_adjustment = player->block_delay_adjustment;
12625
12626       /* if player blocks last field, add delay for exactly one move */
12627       if (player->block_last_field)
12628       {
12629         last_field_block_delay += player->move_delay_value;
12630
12631         /* when blocking enabled, prevent moving up despite gravity */
12632         if (player->gravity && player->MovDir == MV_UP)
12633           block_delay_adjustment = -1;
12634       }
12635
12636       /* add block delay adjustment (also possible when not blocking) */
12637       last_field_block_delay += block_delay_adjustment;
12638
12639       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12640       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12641     }
12642
12643     if (player->MovPos != 0)    /* player has not yet reached destination */
12644       return;
12645   }
12646   else if (!FrameReached(&player->actual_frame_counter, 1))
12647     return;
12648
12649   if (player->MovPos != 0)
12650   {
12651     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12652     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12653
12654     /* before DrawPlayer() to draw correct player graphic for this case */
12655     if (player->MovPos == 0)
12656       CheckGravityMovement(player);
12657   }
12658
12659   if (player->MovPos == 0)      /* player reached destination field */
12660   {
12661     if (player->move_delay_reset_counter > 0)
12662     {
12663       player->move_delay_reset_counter--;
12664
12665       if (player->move_delay_reset_counter == 0)
12666       {
12667         /* continue with normal speed after quickly moving through gate */
12668         HALVE_PLAYER_SPEED(player);
12669
12670         /* be able to make the next move without delay */
12671         player->move_delay = 0;
12672       }
12673     }
12674
12675     player->last_jx = jx;
12676     player->last_jy = jy;
12677
12678     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12679         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12680         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12681         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12682         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12683         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12684         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12685         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12686     {
12687       ExitPlayer(player);
12688
12689       if ((local_player->friends_still_needed == 0 ||
12690            IS_SP_ELEMENT(Feld[jx][jy])) &&
12691           AllPlayersGone)
12692         PlayerWins(local_player);
12693     }
12694
12695     /* this breaks one level: "machine", level 000 */
12696     {
12697       int move_direction = player->MovDir;
12698       int enter_side = MV_DIR_OPPOSITE(move_direction);
12699       int leave_side = move_direction;
12700       int old_jx = last_jx;
12701       int old_jy = last_jy;
12702       int old_element = Feld[old_jx][old_jy];
12703       int new_element = Feld[jx][jy];
12704
12705       if (IS_CUSTOM_ELEMENT(old_element))
12706         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12707                                    CE_LEFT_BY_PLAYER,
12708                                    player->index_bit, leave_side);
12709
12710       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12711                                           CE_PLAYER_LEAVES_X,
12712                                           player->index_bit, leave_side);
12713
12714       if (IS_CUSTOM_ELEMENT(new_element))
12715         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12716                                    player->index_bit, enter_side);
12717
12718       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12719                                           CE_PLAYER_ENTERS_X,
12720                                           player->index_bit, enter_side);
12721
12722       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12723                                         CE_MOVE_OF_X, move_direction);
12724     }
12725
12726     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12727     {
12728       TestIfPlayerTouchesBadThing(jx, jy);
12729       TestIfPlayerTouchesCustomElement(jx, jy);
12730
12731       /* needed because pushed element has not yet reached its destination,
12732          so it would trigger a change event at its previous field location */
12733       if (!player->is_pushing)
12734         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12735
12736       if (!player->active)
12737         RemovePlayer(player);
12738     }
12739
12740     if (!local_player->LevelSolved && level.use_step_counter)
12741     {
12742       int i;
12743
12744       TimePlayed++;
12745
12746       if (TimeLeft > 0)
12747       {
12748         TimeLeft--;
12749
12750         if (TimeLeft <= 10 && setup.time_limit)
12751           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12752
12753         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12754
12755         DisplayGameControlValues();
12756
12757         if (!TimeLeft && setup.time_limit)
12758           for (i = 0; i < MAX_PLAYERS; i++)
12759             KillPlayer(&stored_player[i]);
12760       }
12761       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12762       {
12763         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12764
12765         DisplayGameControlValues();
12766       }
12767     }
12768
12769     if (tape.single_step && tape.recording && !tape.pausing &&
12770         !player->programmed_action)
12771       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12772
12773     if (!player->programmed_action)
12774       CheckSaveEngineSnapshot(player);
12775   }
12776 }
12777
12778 void ScrollScreen(struct PlayerInfo *player, int mode)
12779 {
12780   static unsigned int screen_frame_counter = 0;
12781
12782   if (mode == SCROLL_INIT)
12783   {
12784     /* set scrolling step size according to actual player's moving speed */
12785     ScrollStepSize = TILEX / player->move_delay_value;
12786
12787     screen_frame_counter = FrameCounter;
12788     ScreenMovDir = player->MovDir;
12789     ScreenMovPos = player->MovPos;
12790     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12791     return;
12792   }
12793   else if (!FrameReached(&screen_frame_counter, 1))
12794     return;
12795
12796   if (ScreenMovPos)
12797   {
12798     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12799     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12800     redraw_mask |= REDRAW_FIELD;
12801   }
12802   else
12803     ScreenMovDir = MV_NONE;
12804 }
12805
12806 void TestIfPlayerTouchesCustomElement(int x, int y)
12807 {
12808   static int xy[4][2] =
12809   {
12810     { 0, -1 },
12811     { -1, 0 },
12812     { +1, 0 },
12813     { 0, +1 }
12814   };
12815   static int trigger_sides[4][2] =
12816   {
12817     /* center side       border side */
12818     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12819     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12820     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12821     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12822   };
12823   static int touch_dir[4] =
12824   {
12825     MV_LEFT | MV_RIGHT,
12826     MV_UP   | MV_DOWN,
12827     MV_UP   | MV_DOWN,
12828     MV_LEFT | MV_RIGHT
12829   };
12830   int center_element = Feld[x][y];      /* should always be non-moving! */
12831   int i;
12832
12833   for (i = 0; i < NUM_DIRECTIONS; i++)
12834   {
12835     int xx = x + xy[i][0];
12836     int yy = y + xy[i][1];
12837     int center_side = trigger_sides[i][0];
12838     int border_side = trigger_sides[i][1];
12839     int border_element;
12840
12841     if (!IN_LEV_FIELD(xx, yy))
12842       continue;
12843
12844     if (IS_PLAYER(x, y))                /* player found at center element */
12845     {
12846       struct PlayerInfo *player = PLAYERINFO(x, y);
12847
12848       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12849         border_element = Feld[xx][yy];          /* may be moving! */
12850       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12851         border_element = Feld[xx][yy];
12852       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12853         border_element = MovingOrBlocked2Element(xx, yy);
12854       else
12855         continue;               /* center and border element do not touch */
12856
12857       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12858                                  player->index_bit, border_side);
12859       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12860                                           CE_PLAYER_TOUCHES_X,
12861                                           player->index_bit, border_side);
12862
12863       {
12864         /* use player element that is initially defined in the level playfield,
12865            not the player element that corresponds to the runtime player number
12866            (example: a level that contains EL_PLAYER_3 as the only player would
12867            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12868         int player_element = PLAYERINFO(x, y)->initial_element;
12869
12870         CheckElementChangeBySide(xx, yy, border_element, player_element,
12871                                  CE_TOUCHING_X, border_side);
12872       }
12873     }
12874     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12875     {
12876       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12877
12878       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12879       {
12880         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12881           continue;             /* center and border element do not touch */
12882       }
12883
12884       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12885                                  player->index_bit, center_side);
12886       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12887                                           CE_PLAYER_TOUCHES_X,
12888                                           player->index_bit, center_side);
12889
12890       {
12891         /* use player element that is initially defined in the level playfield,
12892            not the player element that corresponds to the runtime player number
12893            (example: a level that contains EL_PLAYER_3 as the only player would
12894            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12895         int player_element = PLAYERINFO(xx, yy)->initial_element;
12896
12897         CheckElementChangeBySide(x, y, center_element, player_element,
12898                                  CE_TOUCHING_X, center_side);
12899       }
12900
12901       break;
12902     }
12903   }
12904 }
12905
12906 void TestIfElementTouchesCustomElement(int x, int y)
12907 {
12908   static int xy[4][2] =
12909   {
12910     { 0, -1 },
12911     { -1, 0 },
12912     { +1, 0 },
12913     { 0, +1 }
12914   };
12915   static int trigger_sides[4][2] =
12916   {
12917     /* center side      border side */
12918     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12919     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12920     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12921     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12922   };
12923   static int touch_dir[4] =
12924   {
12925     MV_LEFT | MV_RIGHT,
12926     MV_UP   | MV_DOWN,
12927     MV_UP   | MV_DOWN,
12928     MV_LEFT | MV_RIGHT
12929   };
12930   boolean change_center_element = FALSE;
12931   int center_element = Feld[x][y];      /* should always be non-moving! */
12932   int border_element_old[NUM_DIRECTIONS];
12933   int i;
12934
12935   for (i = 0; i < NUM_DIRECTIONS; i++)
12936   {
12937     int xx = x + xy[i][0];
12938     int yy = y + xy[i][1];
12939     int border_element;
12940
12941     border_element_old[i] = -1;
12942
12943     if (!IN_LEV_FIELD(xx, yy))
12944       continue;
12945
12946     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12947       border_element = Feld[xx][yy];    /* may be moving! */
12948     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12949       border_element = Feld[xx][yy];
12950     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12951       border_element = MovingOrBlocked2Element(xx, yy);
12952     else
12953       continue;                 /* center and border element do not touch */
12954
12955     border_element_old[i] = border_element;
12956   }
12957
12958   for (i = 0; i < NUM_DIRECTIONS; i++)
12959   {
12960     int xx = x + xy[i][0];
12961     int yy = y + xy[i][1];
12962     int center_side = trigger_sides[i][0];
12963     int border_element = border_element_old[i];
12964
12965     if (border_element == -1)
12966       continue;
12967
12968     /* check for change of border element */
12969     CheckElementChangeBySide(xx, yy, border_element, center_element,
12970                              CE_TOUCHING_X, center_side);
12971
12972     /* (center element cannot be player, so we dont have to check this here) */
12973   }
12974
12975   for (i = 0; i < NUM_DIRECTIONS; i++)
12976   {
12977     int xx = x + xy[i][0];
12978     int yy = y + xy[i][1];
12979     int border_side = trigger_sides[i][1];
12980     int border_element = border_element_old[i];
12981
12982     if (border_element == -1)
12983       continue;
12984
12985     /* check for change of center element (but change it only once) */
12986     if (!change_center_element)
12987       change_center_element =
12988         CheckElementChangeBySide(x, y, center_element, border_element,
12989                                  CE_TOUCHING_X, border_side);
12990
12991     if (IS_PLAYER(xx, yy))
12992     {
12993       /* use player element that is initially defined in the level playfield,
12994          not the player element that corresponds to the runtime player number
12995          (example: a level that contains EL_PLAYER_3 as the only player would
12996          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12997       int player_element = PLAYERINFO(xx, yy)->initial_element;
12998
12999       CheckElementChangeBySide(x, y, center_element, player_element,
13000                                CE_TOUCHING_X, border_side);
13001     }
13002   }
13003 }
13004
13005 void TestIfElementHitsCustomElement(int x, int y, int direction)
13006 {
13007   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13008   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13009   int hitx = x + dx, hity = y + dy;
13010   int hitting_element = Feld[x][y];
13011   int touched_element;
13012
13013   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13014     return;
13015
13016   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13017                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13018
13019   if (IN_LEV_FIELD(hitx, hity))
13020   {
13021     int opposite_direction = MV_DIR_OPPOSITE(direction);
13022     int hitting_side = direction;
13023     int touched_side = opposite_direction;
13024     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13025                           MovDir[hitx][hity] != direction ||
13026                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13027
13028     object_hit = TRUE;
13029
13030     if (object_hit)
13031     {
13032       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13033                                CE_HITTING_X, touched_side);
13034
13035       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13036                                CE_HIT_BY_X, hitting_side);
13037
13038       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13039                                CE_HIT_BY_SOMETHING, opposite_direction);
13040
13041       if (IS_PLAYER(hitx, hity))
13042       {
13043         /* use player element that is initially defined in the level playfield,
13044            not the player element that corresponds to the runtime player number
13045            (example: a level that contains EL_PLAYER_3 as the only player would
13046            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13047         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13048
13049         CheckElementChangeBySide(x, y, hitting_element, player_element,
13050                                  CE_HITTING_X, touched_side);
13051       }
13052     }
13053   }
13054
13055   /* "hitting something" is also true when hitting the playfield border */
13056   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13057                            CE_HITTING_SOMETHING, direction);
13058 }
13059
13060 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13061 {
13062   int i, kill_x = -1, kill_y = -1;
13063
13064   int bad_element = -1;
13065   static int test_xy[4][2] =
13066   {
13067     { 0, -1 },
13068     { -1, 0 },
13069     { +1, 0 },
13070     { 0, +1 }
13071   };
13072   static int test_dir[4] =
13073   {
13074     MV_UP,
13075     MV_LEFT,
13076     MV_RIGHT,
13077     MV_DOWN
13078   };
13079
13080   for (i = 0; i < NUM_DIRECTIONS; i++)
13081   {
13082     int test_x, test_y, test_move_dir, test_element;
13083
13084     test_x = good_x + test_xy[i][0];
13085     test_y = good_y + test_xy[i][1];
13086
13087     if (!IN_LEV_FIELD(test_x, test_y))
13088       continue;
13089
13090     test_move_dir =
13091       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13092
13093     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13094
13095     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13096        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13097     */
13098     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13099         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13100     {
13101       kill_x = test_x;
13102       kill_y = test_y;
13103       bad_element = test_element;
13104
13105       break;
13106     }
13107   }
13108
13109   if (kill_x != -1 || kill_y != -1)
13110   {
13111     if (IS_PLAYER(good_x, good_y))
13112     {
13113       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13114
13115       if (player->shield_deadly_time_left > 0 &&
13116           !IS_INDESTRUCTIBLE(bad_element))
13117         Bang(kill_x, kill_y);
13118       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13119         KillPlayer(player);
13120     }
13121     else
13122       Bang(good_x, good_y);
13123   }
13124 }
13125
13126 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13127 {
13128   int i, kill_x = -1, kill_y = -1;
13129   int bad_element = Feld[bad_x][bad_y];
13130   static int test_xy[4][2] =
13131   {
13132     { 0, -1 },
13133     { -1, 0 },
13134     { +1, 0 },
13135     { 0, +1 }
13136   };
13137   static int touch_dir[4] =
13138   {
13139     MV_LEFT | MV_RIGHT,
13140     MV_UP   | MV_DOWN,
13141     MV_UP   | MV_DOWN,
13142     MV_LEFT | MV_RIGHT
13143   };
13144   static int test_dir[4] =
13145   {
13146     MV_UP,
13147     MV_LEFT,
13148     MV_RIGHT,
13149     MV_DOWN
13150   };
13151
13152   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13153     return;
13154
13155   for (i = 0; i < NUM_DIRECTIONS; i++)
13156   {
13157     int test_x, test_y, test_move_dir, test_element;
13158
13159     test_x = bad_x + test_xy[i][0];
13160     test_y = bad_y + test_xy[i][1];
13161
13162     if (!IN_LEV_FIELD(test_x, test_y))
13163       continue;
13164
13165     test_move_dir =
13166       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13167
13168     test_element = Feld[test_x][test_y];
13169
13170     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13171        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13172     */
13173     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13174         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13175     {
13176       /* good thing is player or penguin that does not move away */
13177       if (IS_PLAYER(test_x, test_y))
13178       {
13179         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13180
13181         if (bad_element == EL_ROBOT && player->is_moving)
13182           continue;     /* robot does not kill player if he is moving */
13183
13184         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13185         {
13186           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13187             continue;           /* center and border element do not touch */
13188         }
13189
13190         kill_x = test_x;
13191         kill_y = test_y;
13192
13193         break;
13194       }
13195       else if (test_element == EL_PENGUIN)
13196       {
13197         kill_x = test_x;
13198         kill_y = test_y;
13199
13200         break;
13201       }
13202     }
13203   }
13204
13205   if (kill_x != -1 || kill_y != -1)
13206   {
13207     if (IS_PLAYER(kill_x, kill_y))
13208     {
13209       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13210
13211       if (player->shield_deadly_time_left > 0 &&
13212           !IS_INDESTRUCTIBLE(bad_element))
13213         Bang(bad_x, bad_y);
13214       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13215         KillPlayer(player);
13216     }
13217     else
13218       Bang(kill_x, kill_y);
13219   }
13220 }
13221
13222 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13223 {
13224   int bad_element = Feld[bad_x][bad_y];
13225   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13226   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13227   int test_x = bad_x + dx, test_y = bad_y + dy;
13228   int test_move_dir, test_element;
13229   int kill_x = -1, kill_y = -1;
13230
13231   if (!IN_LEV_FIELD(test_x, test_y))
13232     return;
13233
13234   test_move_dir =
13235     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13236
13237   test_element = Feld[test_x][test_y];
13238
13239   if (test_move_dir != bad_move_dir)
13240   {
13241     /* good thing can be player or penguin that does not move away */
13242     if (IS_PLAYER(test_x, test_y))
13243     {
13244       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13245
13246       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13247          player as being hit when he is moving towards the bad thing, because
13248          the "get hit by" condition would be lost after the player stops) */
13249       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13250         return;         /* player moves away from bad thing */
13251
13252       kill_x = test_x;
13253       kill_y = test_y;
13254     }
13255     else if (test_element == EL_PENGUIN)
13256     {
13257       kill_x = test_x;
13258       kill_y = test_y;
13259     }
13260   }
13261
13262   if (kill_x != -1 || kill_y != -1)
13263   {
13264     if (IS_PLAYER(kill_x, kill_y))
13265     {
13266       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13267
13268       if (player->shield_deadly_time_left > 0 &&
13269           !IS_INDESTRUCTIBLE(bad_element))
13270         Bang(bad_x, bad_y);
13271       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13272         KillPlayer(player);
13273     }
13274     else
13275       Bang(kill_x, kill_y);
13276   }
13277 }
13278
13279 void TestIfPlayerTouchesBadThing(int x, int y)
13280 {
13281   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13282 }
13283
13284 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13285 {
13286   TestIfGoodThingHitsBadThing(x, y, move_dir);
13287 }
13288
13289 void TestIfBadThingTouchesPlayer(int x, int y)
13290 {
13291   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13292 }
13293
13294 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13295 {
13296   TestIfBadThingHitsGoodThing(x, y, move_dir);
13297 }
13298
13299 void TestIfFriendTouchesBadThing(int x, int y)
13300 {
13301   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13302 }
13303
13304 void TestIfBadThingTouchesFriend(int x, int y)
13305 {
13306   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13307 }
13308
13309 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13310 {
13311   int i, kill_x = bad_x, kill_y = bad_y;
13312   static int xy[4][2] =
13313   {
13314     { 0, -1 },
13315     { -1, 0 },
13316     { +1, 0 },
13317     { 0, +1 }
13318   };
13319
13320   for (i = 0; i < NUM_DIRECTIONS; i++)
13321   {
13322     int x, y, element;
13323
13324     x = bad_x + xy[i][0];
13325     y = bad_y + xy[i][1];
13326     if (!IN_LEV_FIELD(x, y))
13327       continue;
13328
13329     element = Feld[x][y];
13330     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13331         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13332     {
13333       kill_x = x;
13334       kill_y = y;
13335       break;
13336     }
13337   }
13338
13339   if (kill_x != bad_x || kill_y != bad_y)
13340     Bang(bad_x, bad_y);
13341 }
13342
13343 void KillPlayer(struct PlayerInfo *player)
13344 {
13345   int jx = player->jx, jy = player->jy;
13346
13347   if (!player->active)
13348     return;
13349
13350 #if 0
13351   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13352          player->killed, player->active, player->reanimated);
13353 #endif
13354
13355   /* the following code was introduced to prevent an infinite loop when calling
13356      -> Bang()
13357      -> CheckTriggeredElementChangeExt()
13358      -> ExecuteCustomElementAction()
13359      -> KillPlayer()
13360      -> (infinitely repeating the above sequence of function calls)
13361      which occurs when killing the player while having a CE with the setting
13362      "kill player X when explosion of <player X>"; the solution using a new
13363      field "player->killed" was chosen for backwards compatibility, although
13364      clever use of the fields "player->active" etc. would probably also work */
13365 #if 1
13366   if (player->killed)
13367     return;
13368 #endif
13369
13370   player->killed = TRUE;
13371
13372   /* remove accessible field at the player's position */
13373   Feld[jx][jy] = EL_EMPTY;
13374
13375   /* deactivate shield (else Bang()/Explode() would not work right) */
13376   player->shield_normal_time_left = 0;
13377   player->shield_deadly_time_left = 0;
13378
13379 #if 0
13380   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13381          player->killed, player->active, player->reanimated);
13382 #endif
13383
13384   Bang(jx, jy);
13385
13386 #if 0
13387   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13388          player->killed, player->active, player->reanimated);
13389 #endif
13390
13391   if (player->reanimated)       /* killed player may have been reanimated */
13392     player->killed = player->reanimated = FALSE;
13393   else
13394     BuryPlayer(player);
13395 }
13396
13397 static void KillPlayerUnlessEnemyProtected(int x, int y)
13398 {
13399   if (!PLAYER_ENEMY_PROTECTED(x, y))
13400     KillPlayer(PLAYERINFO(x, y));
13401 }
13402
13403 static void KillPlayerUnlessExplosionProtected(int x, int y)
13404 {
13405   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13406     KillPlayer(PLAYERINFO(x, y));
13407 }
13408
13409 void BuryPlayer(struct PlayerInfo *player)
13410 {
13411   int jx = player->jx, jy = player->jy;
13412
13413   if (!player->active)
13414     return;
13415
13416   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13417   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13418
13419   player->GameOver = TRUE;
13420   RemovePlayer(player);
13421 }
13422
13423 void RemovePlayer(struct PlayerInfo *player)
13424 {
13425   int jx = player->jx, jy = player->jy;
13426   int i, found = FALSE;
13427
13428   player->present = FALSE;
13429   player->active = FALSE;
13430
13431   if (!ExplodeField[jx][jy])
13432     StorePlayer[jx][jy] = 0;
13433
13434   if (player->is_moving)
13435     TEST_DrawLevelField(player->last_jx, player->last_jy);
13436
13437   for (i = 0; i < MAX_PLAYERS; i++)
13438     if (stored_player[i].active)
13439       found = TRUE;
13440
13441   if (!found)
13442     AllPlayersGone = TRUE;
13443
13444   ExitX = ZX = jx;
13445   ExitY = ZY = jy;
13446 }
13447
13448 void ExitPlayer(struct PlayerInfo *player)
13449 {
13450   DrawPlayer(player);   /* needed here only to cleanup last field */
13451   RemovePlayer(player);
13452
13453   if (local_player->players_still_needed > 0)
13454     local_player->players_still_needed--;
13455
13456   /* also set if some players not yet gone, but not needed to solve level */
13457   if (local_player->players_still_needed == 0)
13458     AllPlayersGone = TRUE;
13459 }
13460
13461 static void setFieldForSnapping(int x, int y, int element, int direction)
13462 {
13463   struct ElementInfo *ei = &element_info[element];
13464   int direction_bit = MV_DIR_TO_BIT(direction);
13465   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13466   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13467                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13468
13469   Feld[x][y] = EL_ELEMENT_SNAPPING;
13470   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13471
13472   ResetGfxAnimation(x, y);
13473
13474   GfxElement[x][y] = element;
13475   GfxAction[x][y] = action;
13476   GfxDir[x][y] = direction;
13477   GfxFrame[x][y] = -1;
13478 }
13479
13480 /*
13481   =============================================================================
13482   checkDiagonalPushing()
13483   -----------------------------------------------------------------------------
13484   check if diagonal input device direction results in pushing of object
13485   (by checking if the alternative direction is walkable, diggable, ...)
13486   =============================================================================
13487 */
13488
13489 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13490                                     int x, int y, int real_dx, int real_dy)
13491 {
13492   int jx, jy, dx, dy, xx, yy;
13493
13494   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13495     return TRUE;
13496
13497   /* diagonal direction: check alternative direction */
13498   jx = player->jx;
13499   jy = player->jy;
13500   dx = x - jx;
13501   dy = y - jy;
13502   xx = jx + (dx == 0 ? real_dx : 0);
13503   yy = jy + (dy == 0 ? real_dy : 0);
13504
13505   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13506 }
13507
13508 /*
13509   =============================================================================
13510   DigField()
13511   -----------------------------------------------------------------------------
13512   x, y:                 field next to player (non-diagonal) to try to dig to
13513   real_dx, real_dy:     direction as read from input device (can be diagonal)
13514   =============================================================================
13515 */
13516
13517 static int DigField(struct PlayerInfo *player,
13518                     int oldx, int oldy, int x, int y,
13519                     int real_dx, int real_dy, int mode)
13520 {
13521   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13522   boolean player_was_pushing = player->is_pushing;
13523   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13524   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13525   int jx = oldx, jy = oldy;
13526   int dx = x - jx, dy = y - jy;
13527   int nextx = x + dx, nexty = y + dy;
13528   int move_direction = (dx == -1 ? MV_LEFT  :
13529                         dx == +1 ? MV_RIGHT :
13530                         dy == -1 ? MV_UP    :
13531                         dy == +1 ? MV_DOWN  : MV_NONE);
13532   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13533   int dig_side = MV_DIR_OPPOSITE(move_direction);
13534   int old_element = Feld[jx][jy];
13535   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13536   int collect_count;
13537
13538   if (is_player)                /* function can also be called by EL_PENGUIN */
13539   {
13540     if (player->MovPos == 0)
13541     {
13542       player->is_digging = FALSE;
13543       player->is_collecting = FALSE;
13544     }
13545
13546     if (player->MovPos == 0)    /* last pushing move finished */
13547       player->is_pushing = FALSE;
13548
13549     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13550     {
13551       player->is_switching = FALSE;
13552       player->push_delay = -1;
13553
13554       return MP_NO_ACTION;
13555     }
13556   }
13557
13558   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13559     old_element = Back[jx][jy];
13560
13561   /* in case of element dropped at player position, check background */
13562   else if (Back[jx][jy] != EL_EMPTY &&
13563            game.engine_version >= VERSION_IDENT(2,2,0,0))
13564     old_element = Back[jx][jy];
13565
13566   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13567     return MP_NO_ACTION;        /* field has no opening in this direction */
13568
13569   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13570     return MP_NO_ACTION;        /* field has no opening in this direction */
13571
13572   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13573   {
13574     SplashAcid(x, y);
13575
13576     Feld[jx][jy] = player->artwork_element;
13577     InitMovingField(jx, jy, MV_DOWN);
13578     Store[jx][jy] = EL_ACID;
13579     ContinueMoving(jx, jy);
13580     BuryPlayer(player);
13581
13582     return MP_DONT_RUN_INTO;
13583   }
13584
13585   if (player_can_move && DONT_RUN_INTO(element))
13586   {
13587     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13588
13589     return MP_DONT_RUN_INTO;
13590   }
13591
13592   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13593     return MP_NO_ACTION;
13594
13595   collect_count = element_info[element].collect_count_initial;
13596
13597   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13598     return MP_NO_ACTION;
13599
13600   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13601     player_can_move = player_can_move_or_snap;
13602
13603   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13604       game.engine_version >= VERSION_IDENT(2,2,0,0))
13605   {
13606     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13607                                player->index_bit, dig_side);
13608     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13609                                         player->index_bit, dig_side);
13610
13611     if (element == EL_DC_LANDMINE)
13612       Bang(x, y);
13613
13614     if (Feld[x][y] != element)          /* field changed by snapping */
13615       return MP_ACTION;
13616
13617     return MP_NO_ACTION;
13618   }
13619
13620   if (player->gravity && is_player && !player->is_auto_moving &&
13621       canFallDown(player) && move_direction != MV_DOWN &&
13622       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13623     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13624
13625   if (player_can_move &&
13626       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13627   {
13628     int sound_element = SND_ELEMENT(element);
13629     int sound_action = ACTION_WALKING;
13630
13631     if (IS_RND_GATE(element))
13632     {
13633       if (!player->key[RND_GATE_NR(element)])
13634         return MP_NO_ACTION;
13635     }
13636     else if (IS_RND_GATE_GRAY(element))
13637     {
13638       if (!player->key[RND_GATE_GRAY_NR(element)])
13639         return MP_NO_ACTION;
13640     }
13641     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13642     {
13643       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13644         return MP_NO_ACTION;
13645     }
13646     else if (element == EL_EXIT_OPEN ||
13647              element == EL_EM_EXIT_OPEN ||
13648              element == EL_EM_EXIT_OPENING ||
13649              element == EL_STEEL_EXIT_OPEN ||
13650              element == EL_EM_STEEL_EXIT_OPEN ||
13651              element == EL_EM_STEEL_EXIT_OPENING ||
13652              element == EL_SP_EXIT_OPEN ||
13653              element == EL_SP_EXIT_OPENING)
13654     {
13655       sound_action = ACTION_PASSING;    /* player is passing exit */
13656     }
13657     else if (element == EL_EMPTY)
13658     {
13659       sound_action = ACTION_MOVING;             /* nothing to walk on */
13660     }
13661
13662     /* play sound from background or player, whatever is available */
13663     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13664       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13665     else
13666       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13667   }
13668   else if (player_can_move &&
13669            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13670   {
13671     if (!ACCESS_FROM(element, opposite_direction))
13672       return MP_NO_ACTION;      /* field not accessible from this direction */
13673
13674     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13675       return MP_NO_ACTION;
13676
13677     if (IS_EM_GATE(element))
13678     {
13679       if (!player->key[EM_GATE_NR(element)])
13680         return MP_NO_ACTION;
13681     }
13682     else if (IS_EM_GATE_GRAY(element))
13683     {
13684       if (!player->key[EM_GATE_GRAY_NR(element)])
13685         return MP_NO_ACTION;
13686     }
13687     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13688     {
13689       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13690         return MP_NO_ACTION;
13691     }
13692     else if (IS_EMC_GATE(element))
13693     {
13694       if (!player->key[EMC_GATE_NR(element)])
13695         return MP_NO_ACTION;
13696     }
13697     else if (IS_EMC_GATE_GRAY(element))
13698     {
13699       if (!player->key[EMC_GATE_GRAY_NR(element)])
13700         return MP_NO_ACTION;
13701     }
13702     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13703     {
13704       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13705         return MP_NO_ACTION;
13706     }
13707     else if (element == EL_DC_GATE_WHITE ||
13708              element == EL_DC_GATE_WHITE_GRAY ||
13709              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13710     {
13711       if (player->num_white_keys == 0)
13712         return MP_NO_ACTION;
13713
13714       player->num_white_keys--;
13715     }
13716     else if (IS_SP_PORT(element))
13717     {
13718       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13719           element == EL_SP_GRAVITY_PORT_RIGHT ||
13720           element == EL_SP_GRAVITY_PORT_UP ||
13721           element == EL_SP_GRAVITY_PORT_DOWN)
13722         player->gravity = !player->gravity;
13723       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13724                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13725                element == EL_SP_GRAVITY_ON_PORT_UP ||
13726                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13727         player->gravity = TRUE;
13728       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13729                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13730                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13731                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13732         player->gravity = FALSE;
13733     }
13734
13735     /* automatically move to the next field with double speed */
13736     player->programmed_action = move_direction;
13737
13738     if (player->move_delay_reset_counter == 0)
13739     {
13740       player->move_delay_reset_counter = 2;     /* two double speed steps */
13741
13742       DOUBLE_PLAYER_SPEED(player);
13743     }
13744
13745     PlayLevelSoundAction(x, y, ACTION_PASSING);
13746   }
13747   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13748   {
13749     RemoveField(x, y);
13750
13751     if (mode != DF_SNAP)
13752     {
13753       GfxElement[x][y] = GFX_ELEMENT(element);
13754       player->is_digging = TRUE;
13755     }
13756
13757     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13758
13759     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13760                                         player->index_bit, dig_side);
13761
13762     if (mode == DF_SNAP)
13763     {
13764       if (level.block_snap_field)
13765         setFieldForSnapping(x, y, element, move_direction);
13766       else
13767         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13768
13769       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13770                                           player->index_bit, dig_side);
13771     }
13772   }
13773   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13774   {
13775     RemoveField(x, y);
13776
13777     if (is_player && mode != DF_SNAP)
13778     {
13779       GfxElement[x][y] = element;
13780       player->is_collecting = TRUE;
13781     }
13782
13783     if (element == EL_SPEED_PILL)
13784     {
13785       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13786     }
13787     else if (element == EL_EXTRA_TIME && level.time > 0)
13788     {
13789       TimeLeft += level.extra_time;
13790
13791       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13792
13793       DisplayGameControlValues();
13794     }
13795     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13796     {
13797       player->shield_normal_time_left += level.shield_normal_time;
13798       if (element == EL_SHIELD_DEADLY)
13799         player->shield_deadly_time_left += level.shield_deadly_time;
13800     }
13801     else if (element == EL_DYNAMITE ||
13802              element == EL_EM_DYNAMITE ||
13803              element == EL_SP_DISK_RED)
13804     {
13805       if (player->inventory_size < MAX_INVENTORY_SIZE)
13806         player->inventory_element[player->inventory_size++] = element;
13807
13808       DrawGameDoorValues();
13809     }
13810     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13811     {
13812       player->dynabomb_count++;
13813       player->dynabombs_left++;
13814     }
13815     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13816     {
13817       player->dynabomb_size++;
13818     }
13819     else if (element == EL_DYNABOMB_INCREASE_POWER)
13820     {
13821       player->dynabomb_xl = TRUE;
13822     }
13823     else if (IS_KEY(element))
13824     {
13825       player->key[KEY_NR(element)] = TRUE;
13826
13827       DrawGameDoorValues();
13828     }
13829     else if (element == EL_DC_KEY_WHITE)
13830     {
13831       player->num_white_keys++;
13832
13833       /* display white keys? */
13834       /* DrawGameDoorValues(); */
13835     }
13836     else if (IS_ENVELOPE(element))
13837     {
13838       player->show_envelope = element;
13839     }
13840     else if (element == EL_EMC_LENSES)
13841     {
13842       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13843
13844       RedrawAllInvisibleElementsForLenses();
13845     }
13846     else if (element == EL_EMC_MAGNIFIER)
13847     {
13848       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13849
13850       RedrawAllInvisibleElementsForMagnifier();
13851     }
13852     else if (IS_DROPPABLE(element) ||
13853              IS_THROWABLE(element))     /* can be collected and dropped */
13854     {
13855       int i;
13856
13857       if (collect_count == 0)
13858         player->inventory_infinite_element = element;
13859       else
13860         for (i = 0; i < collect_count; i++)
13861           if (player->inventory_size < MAX_INVENTORY_SIZE)
13862             player->inventory_element[player->inventory_size++] = element;
13863
13864       DrawGameDoorValues();
13865     }
13866     else if (collect_count > 0)
13867     {
13868       local_player->gems_still_needed -= collect_count;
13869       if (local_player->gems_still_needed < 0)
13870         local_player->gems_still_needed = 0;
13871
13872       game.snapshot.collected_item = TRUE;
13873
13874       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13875
13876       DisplayGameControlValues();
13877     }
13878
13879     RaiseScoreElement(element);
13880     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13881
13882     if (is_player)
13883       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13884                                           player->index_bit, dig_side);
13885
13886     if (mode == DF_SNAP)
13887     {
13888       if (level.block_snap_field)
13889         setFieldForSnapping(x, y, element, move_direction);
13890       else
13891         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13892
13893       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13894                                           player->index_bit, dig_side);
13895     }
13896   }
13897   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13898   {
13899     if (mode == DF_SNAP && element != EL_BD_ROCK)
13900       return MP_NO_ACTION;
13901
13902     if (CAN_FALL(element) && dy)
13903       return MP_NO_ACTION;
13904
13905     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13906         !(element == EL_SPRING && level.use_spring_bug))
13907       return MP_NO_ACTION;
13908
13909     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13910         ((move_direction & MV_VERTICAL &&
13911           ((element_info[element].move_pattern & MV_LEFT &&
13912             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13913            (element_info[element].move_pattern & MV_RIGHT &&
13914             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13915          (move_direction & MV_HORIZONTAL &&
13916           ((element_info[element].move_pattern & MV_UP &&
13917             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13918            (element_info[element].move_pattern & MV_DOWN &&
13919             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13920       return MP_NO_ACTION;
13921
13922     /* do not push elements already moving away faster than player */
13923     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13924         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13925       return MP_NO_ACTION;
13926
13927     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13928     {
13929       if (player->push_delay_value == -1 || !player_was_pushing)
13930         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13931     }
13932     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13933     {
13934       if (player->push_delay_value == -1)
13935         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13936     }
13937     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13938     {
13939       if (!player->is_pushing)
13940         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13941     }
13942
13943     player->is_pushing = TRUE;
13944     player->is_active = TRUE;
13945
13946     if (!(IN_LEV_FIELD(nextx, nexty) &&
13947           (IS_FREE(nextx, nexty) ||
13948            (IS_SB_ELEMENT(element) &&
13949             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13950            (IS_CUSTOM_ELEMENT(element) &&
13951             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13952       return MP_NO_ACTION;
13953
13954     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13955       return MP_NO_ACTION;
13956
13957     if (player->push_delay == -1)       /* new pushing; restart delay */
13958       player->push_delay = 0;
13959
13960     if (player->push_delay < player->push_delay_value &&
13961         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13962         element != EL_SPRING && element != EL_BALLOON)
13963     {
13964       /* make sure that there is no move delay before next try to push */
13965       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13966         player->move_delay = 0;
13967
13968       return MP_NO_ACTION;
13969     }
13970
13971     if (IS_CUSTOM_ELEMENT(element) &&
13972         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13973     {
13974       if (!DigFieldByCE(nextx, nexty, element))
13975         return MP_NO_ACTION;
13976     }
13977
13978     if (IS_SB_ELEMENT(element))
13979     {
13980       if (element == EL_SOKOBAN_FIELD_FULL)
13981       {
13982         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13983         local_player->sokobanfields_still_needed++;
13984       }
13985
13986       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13987       {
13988         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13989         local_player->sokobanfields_still_needed--;
13990       }
13991
13992       Feld[x][y] = EL_SOKOBAN_OBJECT;
13993
13994       if (Back[x][y] == Back[nextx][nexty])
13995         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13996       else if (Back[x][y] != 0)
13997         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13998                                     ACTION_EMPTYING);
13999       else
14000         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14001                                     ACTION_FILLING);
14002
14003       if (local_player->sokobanfields_still_needed == 0 &&
14004           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14005       {
14006         local_player->players_still_needed = 0;
14007
14008         PlayerWins(player);
14009
14010         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14011       }
14012     }
14013     else
14014       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14015
14016     InitMovingField(x, y, move_direction);
14017     GfxAction[x][y] = ACTION_PUSHING;
14018
14019     if (mode == DF_SNAP)
14020       ContinueMoving(x, y);
14021     else
14022       MovPos[x][y] = (dx != 0 ? dx : dy);
14023
14024     Pushed[x][y] = TRUE;
14025     Pushed[nextx][nexty] = TRUE;
14026
14027     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14028       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14029     else
14030       player->push_delay_value = -1;    /* get new value later */
14031
14032     /* check for element change _after_ element has been pushed */
14033     if (game.use_change_when_pushing_bug)
14034     {
14035       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14036                                  player->index_bit, dig_side);
14037       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14038                                           player->index_bit, dig_side);
14039     }
14040   }
14041   else if (IS_SWITCHABLE(element))
14042   {
14043     if (PLAYER_SWITCHING(player, x, y))
14044     {
14045       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14046                                           player->index_bit, dig_side);
14047
14048       return MP_ACTION;
14049     }
14050
14051     player->is_switching = TRUE;
14052     player->switch_x = x;
14053     player->switch_y = y;
14054
14055     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14056
14057     if (element == EL_ROBOT_WHEEL)
14058     {
14059       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14060       ZX = x;
14061       ZY = y;
14062
14063       game.robot_wheel_active = TRUE;
14064
14065       TEST_DrawLevelField(x, y);
14066     }
14067     else if (element == EL_SP_TERMINAL)
14068     {
14069       int xx, yy;
14070
14071       SCAN_PLAYFIELD(xx, yy)
14072       {
14073         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14074         {
14075           Bang(xx, yy);
14076         }
14077         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14078         {
14079           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14080
14081           ResetGfxAnimation(xx, yy);
14082           TEST_DrawLevelField(xx, yy);
14083         }
14084       }
14085     }
14086     else if (IS_BELT_SWITCH(element))
14087     {
14088       ToggleBeltSwitch(x, y);
14089     }
14090     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14091              element == EL_SWITCHGATE_SWITCH_DOWN ||
14092              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14093              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14094     {
14095       ToggleSwitchgateSwitch(x, y);
14096     }
14097     else if (element == EL_LIGHT_SWITCH ||
14098              element == EL_LIGHT_SWITCH_ACTIVE)
14099     {
14100       ToggleLightSwitch(x, y);
14101     }
14102     else if (element == EL_TIMEGATE_SWITCH ||
14103              element == EL_DC_TIMEGATE_SWITCH)
14104     {
14105       ActivateTimegateSwitch(x, y);
14106     }
14107     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14108              element == EL_BALLOON_SWITCH_RIGHT ||
14109              element == EL_BALLOON_SWITCH_UP    ||
14110              element == EL_BALLOON_SWITCH_DOWN  ||
14111              element == EL_BALLOON_SWITCH_NONE  ||
14112              element == EL_BALLOON_SWITCH_ANY)
14113     {
14114       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14115                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14116                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14117                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14118                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14119                              move_direction);
14120     }
14121     else if (element == EL_LAMP)
14122     {
14123       Feld[x][y] = EL_LAMP_ACTIVE;
14124       local_player->lights_still_needed--;
14125
14126       ResetGfxAnimation(x, y);
14127       TEST_DrawLevelField(x, y);
14128     }
14129     else if (element == EL_TIME_ORB_FULL)
14130     {
14131       Feld[x][y] = EL_TIME_ORB_EMPTY;
14132
14133       if (level.time > 0 || level.use_time_orb_bug)
14134       {
14135         TimeLeft += level.time_orb_time;
14136         game.no_time_limit = FALSE;
14137
14138         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14139
14140         DisplayGameControlValues();
14141       }
14142
14143       ResetGfxAnimation(x, y);
14144       TEST_DrawLevelField(x, y);
14145     }
14146     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14147              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14148     {
14149       int xx, yy;
14150
14151       game.ball_state = !game.ball_state;
14152
14153       SCAN_PLAYFIELD(xx, yy)
14154       {
14155         int e = Feld[xx][yy];
14156
14157         if (game.ball_state)
14158         {
14159           if (e == EL_EMC_MAGIC_BALL)
14160             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14161           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14162             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14163         }
14164         else
14165         {
14166           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14167             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14168           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14169             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14170         }
14171       }
14172     }
14173
14174     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14175                                         player->index_bit, dig_side);
14176
14177     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14178                                         player->index_bit, dig_side);
14179
14180     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14181                                         player->index_bit, dig_side);
14182
14183     return MP_ACTION;
14184   }
14185   else
14186   {
14187     if (!PLAYER_SWITCHING(player, x, y))
14188     {
14189       player->is_switching = TRUE;
14190       player->switch_x = x;
14191       player->switch_y = y;
14192
14193       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14194                                  player->index_bit, dig_side);
14195       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14196                                           player->index_bit, dig_side);
14197
14198       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14199                                  player->index_bit, dig_side);
14200       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14201                                           player->index_bit, dig_side);
14202     }
14203
14204     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14205                                player->index_bit, dig_side);
14206     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14207                                         player->index_bit, dig_side);
14208
14209     return MP_NO_ACTION;
14210   }
14211
14212   player->push_delay = -1;
14213
14214   if (is_player)                /* function can also be called by EL_PENGUIN */
14215   {
14216     if (Feld[x][y] != element)          /* really digged/collected something */
14217     {
14218       player->is_collecting = !player->is_digging;
14219       player->is_active = TRUE;
14220     }
14221   }
14222
14223   return MP_MOVING;
14224 }
14225
14226 static boolean DigFieldByCE(int x, int y, int digging_element)
14227 {
14228   int element = Feld[x][y];
14229
14230   if (!IS_FREE(x, y))
14231   {
14232     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14233                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14234                   ACTION_BREAKING);
14235
14236     /* no element can dig solid indestructible elements */
14237     if (IS_INDESTRUCTIBLE(element) &&
14238         !IS_DIGGABLE(element) &&
14239         !IS_COLLECTIBLE(element))
14240       return FALSE;
14241
14242     if (AmoebaNr[x][y] &&
14243         (element == EL_AMOEBA_FULL ||
14244          element == EL_BD_AMOEBA ||
14245          element == EL_AMOEBA_GROWING))
14246     {
14247       AmoebaCnt[AmoebaNr[x][y]]--;
14248       AmoebaCnt2[AmoebaNr[x][y]]--;
14249     }
14250
14251     if (IS_MOVING(x, y))
14252       RemoveMovingField(x, y);
14253     else
14254     {
14255       RemoveField(x, y);
14256       TEST_DrawLevelField(x, y);
14257     }
14258
14259     /* if digged element was about to explode, prevent the explosion */
14260     ExplodeField[x][y] = EX_TYPE_NONE;
14261
14262     PlayLevelSoundAction(x, y, action);
14263   }
14264
14265   Store[x][y] = EL_EMPTY;
14266
14267   /* this makes it possible to leave the removed element again */
14268   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14269     Store[x][y] = element;
14270
14271   return TRUE;
14272 }
14273
14274 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14275 {
14276   int jx = player->jx, jy = player->jy;
14277   int x = jx + dx, y = jy + dy;
14278   int snap_direction = (dx == -1 ? MV_LEFT  :
14279                         dx == +1 ? MV_RIGHT :
14280                         dy == -1 ? MV_UP    :
14281                         dy == +1 ? MV_DOWN  : MV_NONE);
14282   boolean can_continue_snapping = (level.continuous_snapping &&
14283                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14284
14285   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14286     return FALSE;
14287
14288   if (!player->active || !IN_LEV_FIELD(x, y))
14289     return FALSE;
14290
14291   if (dx && dy)
14292     return FALSE;
14293
14294   if (!dx && !dy)
14295   {
14296     if (player->MovPos == 0)
14297       player->is_pushing = FALSE;
14298
14299     player->is_snapping = FALSE;
14300
14301     if (player->MovPos == 0)
14302     {
14303       player->is_moving = FALSE;
14304       player->is_digging = FALSE;
14305       player->is_collecting = FALSE;
14306     }
14307
14308     return FALSE;
14309   }
14310
14311   /* prevent snapping with already pressed snap key when not allowed */
14312   if (player->is_snapping && !can_continue_snapping)
14313     return FALSE;
14314
14315   player->MovDir = snap_direction;
14316
14317   if (player->MovPos == 0)
14318   {
14319     player->is_moving = FALSE;
14320     player->is_digging = FALSE;
14321     player->is_collecting = FALSE;
14322   }
14323
14324   player->is_dropping = FALSE;
14325   player->is_dropping_pressed = FALSE;
14326   player->drop_pressed_delay = 0;
14327
14328   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14329     return FALSE;
14330
14331   player->is_snapping = TRUE;
14332   player->is_active = TRUE;
14333
14334   if (player->MovPos == 0)
14335   {
14336     player->is_moving = FALSE;
14337     player->is_digging = FALSE;
14338     player->is_collecting = FALSE;
14339   }
14340
14341   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14342     TEST_DrawLevelField(player->last_jx, player->last_jy);
14343
14344   TEST_DrawLevelField(x, y);
14345
14346   return TRUE;
14347 }
14348
14349 static boolean DropElement(struct PlayerInfo *player)
14350 {
14351   int old_element, new_element;
14352   int dropx = player->jx, dropy = player->jy;
14353   int drop_direction = player->MovDir;
14354   int drop_side = drop_direction;
14355   int drop_element = get_next_dropped_element(player);
14356
14357   /* do not drop an element on top of another element; when holding drop key
14358      pressed without moving, dropped element must move away before the next
14359      element can be dropped (this is especially important if the next element
14360      is dynamite, which can be placed on background for historical reasons) */
14361   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14362     return MP_ACTION;
14363
14364   if (IS_THROWABLE(drop_element))
14365   {
14366     dropx += GET_DX_FROM_DIR(drop_direction);
14367     dropy += GET_DY_FROM_DIR(drop_direction);
14368
14369     if (!IN_LEV_FIELD(dropx, dropy))
14370       return FALSE;
14371   }
14372
14373   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14374   new_element = drop_element;           /* default: no change when dropping */
14375
14376   /* check if player is active, not moving and ready to drop */
14377   if (!player->active || player->MovPos || player->drop_delay > 0)
14378     return FALSE;
14379
14380   /* check if player has anything that can be dropped */
14381   if (new_element == EL_UNDEFINED)
14382     return FALSE;
14383
14384   /* only set if player has anything that can be dropped */
14385   player->is_dropping_pressed = TRUE;
14386
14387   /* check if drop key was pressed long enough for EM style dynamite */
14388   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14389     return FALSE;
14390
14391   /* check if anything can be dropped at the current position */
14392   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14393     return FALSE;
14394
14395   /* collected custom elements can only be dropped on empty fields */
14396   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14397     return FALSE;
14398
14399   if (old_element != EL_EMPTY)
14400     Back[dropx][dropy] = old_element;   /* store old element on this field */
14401
14402   ResetGfxAnimation(dropx, dropy);
14403   ResetRandomAnimationValue(dropx, dropy);
14404
14405   if (player->inventory_size > 0 ||
14406       player->inventory_infinite_element != EL_UNDEFINED)
14407   {
14408     if (player->inventory_size > 0)
14409     {
14410       player->inventory_size--;
14411
14412       DrawGameDoorValues();
14413
14414       if (new_element == EL_DYNAMITE)
14415         new_element = EL_DYNAMITE_ACTIVE;
14416       else if (new_element == EL_EM_DYNAMITE)
14417         new_element = EL_EM_DYNAMITE_ACTIVE;
14418       else if (new_element == EL_SP_DISK_RED)
14419         new_element = EL_SP_DISK_RED_ACTIVE;
14420     }
14421
14422     Feld[dropx][dropy] = new_element;
14423
14424     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14425       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14426                           el2img(Feld[dropx][dropy]), 0);
14427
14428     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14429
14430     /* needed if previous element just changed to "empty" in the last frame */
14431     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14432
14433     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14434                                player->index_bit, drop_side);
14435     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14436                                         CE_PLAYER_DROPS_X,
14437                                         player->index_bit, drop_side);
14438
14439     TestIfElementTouchesCustomElement(dropx, dropy);
14440   }
14441   else          /* player is dropping a dyna bomb */
14442   {
14443     player->dynabombs_left--;
14444
14445     Feld[dropx][dropy] = new_element;
14446
14447     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14448       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14449                           el2img(Feld[dropx][dropy]), 0);
14450
14451     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14452   }
14453
14454   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14455     InitField_WithBug1(dropx, dropy, FALSE);
14456
14457   new_element = Feld[dropx][dropy];     /* element might have changed */
14458
14459   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14460       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14461   {
14462     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14463       MovDir[dropx][dropy] = drop_direction;
14464
14465     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14466
14467     /* do not cause impact style collision by dropping elements that can fall */
14468     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14469   }
14470
14471   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14472   player->is_dropping = TRUE;
14473
14474   player->drop_pressed_delay = 0;
14475   player->is_dropping_pressed = FALSE;
14476
14477   player->drop_x = dropx;
14478   player->drop_y = dropy;
14479
14480   return TRUE;
14481 }
14482
14483 /* ------------------------------------------------------------------------- */
14484 /* game sound playing functions                                              */
14485 /* ------------------------------------------------------------------------- */
14486
14487 static int *loop_sound_frame = NULL;
14488 static int *loop_sound_volume = NULL;
14489
14490 void InitPlayLevelSound(void)
14491 {
14492   int num_sounds = getSoundListSize();
14493
14494   checked_free(loop_sound_frame);
14495   checked_free(loop_sound_volume);
14496
14497   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14498   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14499 }
14500
14501 static void PlayLevelSound(int x, int y, int nr)
14502 {
14503   int sx = SCREENX(x), sy = SCREENY(y);
14504   int volume, stereo_position;
14505   int max_distance = 8;
14506   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14507
14508   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14509       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14510     return;
14511
14512   if (!IN_LEV_FIELD(x, y) ||
14513       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14514       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14515     return;
14516
14517   volume = SOUND_MAX_VOLUME;
14518
14519   if (!IN_SCR_FIELD(sx, sy))
14520   {
14521     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14522     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14523
14524     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14525   }
14526
14527   stereo_position = (SOUND_MAX_LEFT +
14528                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14529                      (SCR_FIELDX + 2 * max_distance));
14530
14531   if (IS_LOOP_SOUND(nr))
14532   {
14533     /* This assures that quieter loop sounds do not overwrite louder ones,
14534        while restarting sound volume comparison with each new game frame. */
14535
14536     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14537       return;
14538
14539     loop_sound_volume[nr] = volume;
14540     loop_sound_frame[nr] = FrameCounter;
14541   }
14542
14543   PlaySoundExt(nr, volume, stereo_position, type);
14544 }
14545
14546 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14547 {
14548   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14549                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14550                  y < LEVELY(BY1) ? LEVELY(BY1) :
14551                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14552                  sound_action);
14553 }
14554
14555 static void PlayLevelSoundAction(int x, int y, int action)
14556 {
14557   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14558 }
14559
14560 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14561 {
14562   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14563
14564   if (sound_effect != SND_UNDEFINED)
14565     PlayLevelSound(x, y, sound_effect);
14566 }
14567
14568 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14569                                               int action)
14570 {
14571   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14572
14573   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14574     PlayLevelSound(x, y, sound_effect);
14575 }
14576
14577 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14578 {
14579   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14580
14581   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14582     PlayLevelSound(x, y, sound_effect);
14583 }
14584
14585 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14586 {
14587   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14588
14589   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14590     StopSound(sound_effect);
14591 }
14592
14593 static int getLevelMusicNr(void)
14594 {
14595   if (levelset.music[level_nr] != MUS_UNDEFINED)
14596     return levelset.music[level_nr];            /* from config file */
14597   else
14598     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14599 }
14600
14601 static void FadeLevelSounds(void)
14602 {
14603   FadeSounds();
14604 }
14605
14606 static void FadeLevelMusic(void)
14607 {
14608   int music_nr = getLevelMusicNr();
14609   char *curr_music = getCurrentlyPlayingMusicFilename();
14610   char *next_music = getMusicInfoEntryFilename(music_nr);
14611
14612   if (!strEqual(curr_music, next_music))
14613     FadeMusic();
14614 }
14615
14616 void FadeLevelSoundsAndMusic(void)
14617 {
14618   FadeLevelSounds();
14619   FadeLevelMusic();
14620 }
14621
14622 static void PlayLevelMusic(void)
14623 {
14624   int music_nr = getLevelMusicNr();
14625   char *curr_music = getCurrentlyPlayingMusicFilename();
14626   char *next_music = getMusicInfoEntryFilename(music_nr);
14627
14628   if (!strEqual(curr_music, next_music))
14629     PlayMusicLoop(music_nr);
14630 }
14631
14632 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14633 {
14634   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14635   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14636   int x = xx - 1 - offset;
14637   int y = yy - 1 - offset;
14638
14639   switch (sample)
14640   {
14641     case SAMPLE_blank:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14643       break;
14644
14645     case SAMPLE_roll:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14647       break;
14648
14649     case SAMPLE_stone:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14651       break;
14652
14653     case SAMPLE_nut:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14655       break;
14656
14657     case SAMPLE_crack:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14659       break;
14660
14661     case SAMPLE_bug:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14663       break;
14664
14665     case SAMPLE_tank:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14667       break;
14668
14669     case SAMPLE_android_clone:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14671       break;
14672
14673     case SAMPLE_android_move:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14675       break;
14676
14677     case SAMPLE_spring:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14679       break;
14680
14681     case SAMPLE_slurp:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14683       break;
14684
14685     case SAMPLE_eater:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14687       break;
14688
14689     case SAMPLE_eater_eat:
14690       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14691       break;
14692
14693     case SAMPLE_alien:
14694       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14695       break;
14696
14697     case SAMPLE_collect:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14699       break;
14700
14701     case SAMPLE_diamond:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14703       break;
14704
14705     case SAMPLE_squash:
14706       /* !!! CHECK THIS !!! */
14707 #if 1
14708       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14709 #else
14710       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14711 #endif
14712       break;
14713
14714     case SAMPLE_wonderfall:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14716       break;
14717
14718     case SAMPLE_drip:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14720       break;
14721
14722     case SAMPLE_push:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14724       break;
14725
14726     case SAMPLE_dirt:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14728       break;
14729
14730     case SAMPLE_acid:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14732       break;
14733
14734     case SAMPLE_ball:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14736       break;
14737
14738     case SAMPLE_grow:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14740       break;
14741
14742     case SAMPLE_wonder:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14744       break;
14745
14746     case SAMPLE_door:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14748       break;
14749
14750     case SAMPLE_exit_open:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14752       break;
14753
14754     case SAMPLE_exit_leave:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14756       break;
14757
14758     case SAMPLE_dynamite:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14760       break;
14761
14762     case SAMPLE_tick:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14764       break;
14765
14766     case SAMPLE_press:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14768       break;
14769
14770     case SAMPLE_wheel:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14772       break;
14773
14774     case SAMPLE_boom:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14776       break;
14777
14778     case SAMPLE_die:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14780       break;
14781
14782     case SAMPLE_time:
14783       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14784       break;
14785
14786     default:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14788       break;
14789   }
14790 }
14791
14792 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14793 {
14794   int element = map_element_SP_to_RND(element_sp);
14795   int action = map_action_SP_to_RND(action_sp);
14796   int offset = (setup.sp_show_border_elements ? 0 : 1);
14797   int x = xx - offset;
14798   int y = yy - offset;
14799
14800   PlayLevelSoundElementAction(x, y, element, action);
14801 }
14802
14803 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14804 {
14805   int element = map_element_MM_to_RND(element_mm);
14806   int action = map_action_MM_to_RND(action_mm);
14807   int offset = 0;
14808   int x = xx - offset;
14809   int y = yy - offset;
14810
14811   if (!IS_MM_ELEMENT(element))
14812     element = EL_MM_DEFAULT;
14813
14814   PlayLevelSoundElementAction(x, y, element, action);
14815 }
14816
14817 void PlaySound_MM(int sound_mm)
14818 {
14819   int sound = map_sound_MM_to_RND(sound_mm);
14820
14821   if (sound == SND_UNDEFINED)
14822     return;
14823
14824   PlaySound(sound);
14825 }
14826
14827 void PlaySoundLoop_MM(int sound_mm)
14828 {
14829   int sound = map_sound_MM_to_RND(sound_mm);
14830
14831   if (sound == SND_UNDEFINED)
14832     return;
14833
14834   PlaySoundLoop(sound);
14835 }
14836
14837 void StopSound_MM(int sound_mm)
14838 {
14839   int sound = map_sound_MM_to_RND(sound_mm);
14840
14841   if (sound == SND_UNDEFINED)
14842     return;
14843
14844   StopSound(sound);
14845 }
14846
14847 void RaiseScore(int value)
14848 {
14849   local_player->score += value;
14850
14851   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14852
14853   DisplayGameControlValues();
14854 }
14855
14856 void RaiseScoreElement(int element)
14857 {
14858   switch (element)
14859   {
14860     case EL_EMERALD:
14861     case EL_BD_DIAMOND:
14862     case EL_EMERALD_YELLOW:
14863     case EL_EMERALD_RED:
14864     case EL_EMERALD_PURPLE:
14865     case EL_SP_INFOTRON:
14866       RaiseScore(level.score[SC_EMERALD]);
14867       break;
14868     case EL_DIAMOND:
14869       RaiseScore(level.score[SC_DIAMOND]);
14870       break;
14871     case EL_CRYSTAL:
14872       RaiseScore(level.score[SC_CRYSTAL]);
14873       break;
14874     case EL_PEARL:
14875       RaiseScore(level.score[SC_PEARL]);
14876       break;
14877     case EL_BUG:
14878     case EL_BD_BUTTERFLY:
14879     case EL_SP_ELECTRON:
14880       RaiseScore(level.score[SC_BUG]);
14881       break;
14882     case EL_SPACESHIP:
14883     case EL_BD_FIREFLY:
14884     case EL_SP_SNIKSNAK:
14885       RaiseScore(level.score[SC_SPACESHIP]);
14886       break;
14887     case EL_YAMYAM:
14888     case EL_DARK_YAMYAM:
14889       RaiseScore(level.score[SC_YAMYAM]);
14890       break;
14891     case EL_ROBOT:
14892       RaiseScore(level.score[SC_ROBOT]);
14893       break;
14894     case EL_PACMAN:
14895       RaiseScore(level.score[SC_PACMAN]);
14896       break;
14897     case EL_NUT:
14898       RaiseScore(level.score[SC_NUT]);
14899       break;
14900     case EL_DYNAMITE:
14901     case EL_EM_DYNAMITE:
14902     case EL_SP_DISK_RED:
14903     case EL_DYNABOMB_INCREASE_NUMBER:
14904     case EL_DYNABOMB_INCREASE_SIZE:
14905     case EL_DYNABOMB_INCREASE_POWER:
14906       RaiseScore(level.score[SC_DYNAMITE]);
14907       break;
14908     case EL_SHIELD_NORMAL:
14909     case EL_SHIELD_DEADLY:
14910       RaiseScore(level.score[SC_SHIELD]);
14911       break;
14912     case EL_EXTRA_TIME:
14913       RaiseScore(level.extra_time_score);
14914       break;
14915     case EL_KEY_1:
14916     case EL_KEY_2:
14917     case EL_KEY_3:
14918     case EL_KEY_4:
14919     case EL_EM_KEY_1:
14920     case EL_EM_KEY_2:
14921     case EL_EM_KEY_3:
14922     case EL_EM_KEY_4:
14923     case EL_EMC_KEY_5:
14924     case EL_EMC_KEY_6:
14925     case EL_EMC_KEY_7:
14926     case EL_EMC_KEY_8:
14927     case EL_DC_KEY_WHITE:
14928       RaiseScore(level.score[SC_KEY]);
14929       break;
14930     default:
14931       RaiseScore(element_info[element].collect_score);
14932       break;
14933   }
14934 }
14935
14936 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14937 {
14938   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14939   {
14940     /* closing door required in case of envelope style request dialogs */
14941     if (!skip_request)
14942       CloseDoor(DOOR_CLOSE_1);
14943
14944     if (network.enabled)
14945       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14946     else
14947     {
14948       if (quick_quit)
14949         FadeSkipNextFadeIn();
14950
14951       SetGameStatus(GAME_MODE_MAIN);
14952
14953       DrawMainMenu();
14954     }
14955   }
14956   else          /* continue playing the game */
14957   {
14958     if (tape.playing && tape.deactivate_display)
14959       TapeDeactivateDisplayOff(TRUE);
14960
14961     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14962
14963     if (tape.playing && tape.deactivate_display)
14964       TapeDeactivateDisplayOn();
14965   }
14966 }
14967
14968 void RequestQuitGame(boolean ask_if_really_quit)
14969 {
14970   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14971   boolean skip_request = AllPlayersGone || quick_quit;
14972
14973   RequestQuitGameExt(skip_request, quick_quit,
14974                      "Do you really want to quit the game?");
14975 }
14976
14977 void RequestRestartGame(char *message)
14978 {
14979   game.restart_game_message = NULL;
14980
14981   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14982   {
14983     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14984   }
14985   else
14986   {
14987     SetGameStatus(GAME_MODE_MAIN);
14988
14989     DrawMainMenu();
14990   }
14991 }
14992
14993
14994 /* ------------------------------------------------------------------------- */
14995 /* random generator functions                                                */
14996 /* ------------------------------------------------------------------------- */
14997
14998 unsigned int InitEngineRandom_RND(int seed)
14999 {
15000   game.num_random_calls = 0;
15001
15002   return InitEngineRandom(seed);
15003 }
15004
15005 unsigned int RND(int max)
15006 {
15007   if (max > 0)
15008   {
15009     game.num_random_calls++;
15010
15011     return GetEngineRandom(max);
15012   }
15013
15014   return 0;
15015 }
15016
15017
15018 /* ------------------------------------------------------------------------- */
15019 /* game engine snapshot handling functions                                   */
15020 /* ------------------------------------------------------------------------- */
15021
15022 struct EngineSnapshotInfo
15023 {
15024   /* runtime values for custom element collect score */
15025   int collect_score[NUM_CUSTOM_ELEMENTS];
15026
15027   /* runtime values for group element choice position */
15028   int choice_pos[NUM_GROUP_ELEMENTS];
15029
15030   /* runtime values for belt position animations */
15031   int belt_graphic[4][NUM_BELT_PARTS];
15032   int belt_anim_mode[4][NUM_BELT_PARTS];
15033 };
15034
15035 static struct EngineSnapshotInfo engine_snapshot_rnd;
15036 static char *snapshot_level_identifier = NULL;
15037 static int snapshot_level_nr = -1;
15038
15039 static void SaveEngineSnapshotValues_RND(void)
15040 {
15041   static int belt_base_active_element[4] =
15042   {
15043     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15044     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15045     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15046     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15047   };
15048   int i, j;
15049
15050   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15051   {
15052     int element = EL_CUSTOM_START + i;
15053
15054     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15055   }
15056
15057   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15058   {
15059     int element = EL_GROUP_START + i;
15060
15061     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15062   }
15063
15064   for (i = 0; i < 4; i++)
15065   {
15066     for (j = 0; j < NUM_BELT_PARTS; j++)
15067     {
15068       int element = belt_base_active_element[i] + j;
15069       int graphic = el2img(element);
15070       int anim_mode = graphic_info[graphic].anim_mode;
15071
15072       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15073       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15074     }
15075   }
15076 }
15077
15078 static void LoadEngineSnapshotValues_RND(void)
15079 {
15080   unsigned int num_random_calls = game.num_random_calls;
15081   int i, j;
15082
15083   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15084   {
15085     int element = EL_CUSTOM_START + i;
15086
15087     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15088   }
15089
15090   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15091   {
15092     int element = EL_GROUP_START + i;
15093
15094     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15095   }
15096
15097   for (i = 0; i < 4; i++)
15098   {
15099     for (j = 0; j < NUM_BELT_PARTS; j++)
15100     {
15101       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15102       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15103
15104       graphic_info[graphic].anim_mode = anim_mode;
15105     }
15106   }
15107
15108   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15109   {
15110     InitRND(tape.random_seed);
15111     for (i = 0; i < num_random_calls; i++)
15112       RND(1);
15113   }
15114
15115   if (game.num_random_calls != num_random_calls)
15116   {
15117     Error(ERR_INFO, "number of random calls out of sync");
15118     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15119     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15120     Error(ERR_EXIT, "this should not happen -- please debug");
15121   }
15122 }
15123
15124 void FreeEngineSnapshotSingle(void)
15125 {
15126   FreeSnapshotSingle();
15127
15128   setString(&snapshot_level_identifier, NULL);
15129   snapshot_level_nr = -1;
15130 }
15131
15132 void FreeEngineSnapshotList(void)
15133 {
15134   FreeSnapshotList();
15135 }
15136
15137 static ListNode *SaveEngineSnapshotBuffers(void)
15138 {
15139   ListNode *buffers = NULL;
15140
15141   /* copy some special values to a structure better suited for the snapshot */
15142
15143   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15144     SaveEngineSnapshotValues_RND();
15145   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15146     SaveEngineSnapshotValues_EM();
15147   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15148     SaveEngineSnapshotValues_SP(&buffers);
15149   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15150     SaveEngineSnapshotValues_MM(&buffers);
15151
15152   /* save values stored in special snapshot structure */
15153
15154   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15155     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15156   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15157     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15158   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15159     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15160   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15161     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15162
15163   /* save further RND engine values */
15164
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15168
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15173
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15179
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15183
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15185
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15187
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15190
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15194   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15197   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15201   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15204   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15209
15210   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15212
15213   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15214   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15215   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15216
15217   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15218   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15219
15220   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15221   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15222   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15223   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15224   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15225
15226   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15227   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15228
15229 #if 0
15230   ListNode *node = engine_snapshot_list_rnd;
15231   int num_bytes = 0;
15232
15233   while (node != NULL)
15234   {
15235     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15236
15237     node = node->next;
15238   }
15239
15240   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15241 #endif
15242
15243   return buffers;
15244 }
15245
15246 void SaveEngineSnapshotSingle(void)
15247 {
15248   ListNode *buffers = SaveEngineSnapshotBuffers();
15249
15250   /* finally save all snapshot buffers to single snapshot */
15251   SaveSnapshotSingle(buffers);
15252
15253   /* save level identification information */
15254   setString(&snapshot_level_identifier, leveldir_current->identifier);
15255   snapshot_level_nr = level_nr;
15256 }
15257
15258 boolean CheckSaveEngineSnapshotToList(void)
15259 {
15260   boolean save_snapshot =
15261     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15262      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15263       game.snapshot.changed_action) ||
15264      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15265       game.snapshot.collected_item));
15266
15267   game.snapshot.changed_action = FALSE;
15268   game.snapshot.collected_item = FALSE;
15269   game.snapshot.save_snapshot = save_snapshot;
15270
15271   return save_snapshot;
15272 }
15273
15274 void SaveEngineSnapshotToList(void)
15275 {
15276   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15277       tape.quick_resume)
15278     return;
15279
15280   ListNode *buffers = SaveEngineSnapshotBuffers();
15281
15282   /* finally save all snapshot buffers to snapshot list */
15283   SaveSnapshotToList(buffers);
15284 }
15285
15286 void SaveEngineSnapshotToListInitial(void)
15287 {
15288   FreeEngineSnapshotList();
15289
15290   SaveEngineSnapshotToList();
15291 }
15292
15293 static void LoadEngineSnapshotValues(void)
15294 {
15295   /* restore special values from snapshot structure */
15296
15297   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15298     LoadEngineSnapshotValues_RND();
15299   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15300     LoadEngineSnapshotValues_EM();
15301   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15302     LoadEngineSnapshotValues_SP();
15303   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15304     LoadEngineSnapshotValues_MM();
15305 }
15306
15307 void LoadEngineSnapshotSingle(void)
15308 {
15309   LoadSnapshotSingle();
15310
15311   LoadEngineSnapshotValues();
15312 }
15313
15314 static void LoadEngineSnapshot_Undo(int steps)
15315 {
15316   LoadSnapshotFromList_Older(steps);
15317
15318   LoadEngineSnapshotValues();
15319 }
15320
15321 static void LoadEngineSnapshot_Redo(int steps)
15322 {
15323   LoadSnapshotFromList_Newer(steps);
15324
15325   LoadEngineSnapshotValues();
15326 }
15327
15328 boolean CheckEngineSnapshotSingle(void)
15329 {
15330   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15331           snapshot_level_nr == level_nr);
15332 }
15333
15334 boolean CheckEngineSnapshotList(void)
15335 {
15336   return CheckSnapshotList();
15337 }
15338
15339
15340 /* ---------- new game button stuff ---------------------------------------- */
15341
15342 static struct
15343 {
15344   int graphic;
15345   struct XY *pos;
15346   int gadget_id;
15347   boolean *setup_value;
15348   boolean allowed_on_tape;
15349   char *infotext;
15350 } gamebutton_info[NUM_GAME_BUTTONS] =
15351 {
15352   {
15353     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15354     GAME_CTRL_ID_STOP,                          NULL,
15355     TRUE,                                       "stop game"
15356   },
15357   {
15358     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15359     GAME_CTRL_ID_PAUSE,                         NULL,
15360     TRUE,                                       "pause game"
15361   },
15362   {
15363     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15364     GAME_CTRL_ID_PLAY,                          NULL,
15365     TRUE,                                       "play game"
15366   },
15367   {
15368     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15369     GAME_CTRL_ID_UNDO,                          NULL,
15370     TRUE,                                       "undo step"
15371   },
15372   {
15373     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15374     GAME_CTRL_ID_REDO,                          NULL,
15375     TRUE,                                       "redo step"
15376   },
15377   {
15378     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15379     GAME_CTRL_ID_SAVE,                          NULL,
15380     TRUE,                                       "save game"
15381   },
15382   {
15383     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15384     GAME_CTRL_ID_PAUSE2,                        NULL,
15385     TRUE,                                       "pause game"
15386   },
15387   {
15388     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15389     GAME_CTRL_ID_LOAD,                          NULL,
15390     TRUE,                                       "load game"
15391   },
15392   {
15393     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15394     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15395     FALSE,                                      "stop game"
15396   },
15397   {
15398     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15399     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15400     FALSE,                                      "pause game"
15401   },
15402   {
15403     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15404     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15405     FALSE,                                      "play game"
15406   },
15407   {
15408     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15409     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15410     TRUE,                                       "background music on/off"
15411   },
15412   {
15413     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15414     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15415     TRUE,                                       "sound loops on/off"
15416   },
15417   {
15418     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15419     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15420     TRUE,                                       "normal sounds on/off"
15421   },
15422   {
15423     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15424     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15425     FALSE,                                      "background music on/off"
15426   },
15427   {
15428     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15429     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15430     FALSE,                                      "sound loops on/off"
15431   },
15432   {
15433     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15434     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15435     FALSE,                                      "normal sounds on/off"
15436   }
15437 };
15438
15439 void CreateGameButtons(void)
15440 {
15441   int i;
15442
15443   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15444   {
15445     int graphic = gamebutton_info[i].graphic;
15446     struct GraphicInfo *gfx = &graphic_info[graphic];
15447     struct XY *pos = gamebutton_info[i].pos;
15448     struct GadgetInfo *gi;
15449     int button_type;
15450     boolean checked;
15451     unsigned int event_mask;
15452     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15453     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15454     int base_x = (on_tape ? VX : DX);
15455     int base_y = (on_tape ? VY : DY);
15456     int gd_x   = gfx->src_x;
15457     int gd_y   = gfx->src_y;
15458     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15459     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15460     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15461     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15462     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15463     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15464     int id = i;
15465
15466     if (gfx->bitmap == NULL)
15467     {
15468       game_gadget[id] = NULL;
15469
15470       continue;
15471     }
15472
15473     if (id == GAME_CTRL_ID_STOP ||
15474         id == GAME_CTRL_ID_PANEL_STOP ||
15475         id == GAME_CTRL_ID_PLAY ||
15476         id == GAME_CTRL_ID_PANEL_PLAY ||
15477         id == GAME_CTRL_ID_SAVE ||
15478         id == GAME_CTRL_ID_LOAD)
15479     {
15480       button_type = GD_TYPE_NORMAL_BUTTON;
15481       checked = FALSE;
15482       event_mask = GD_EVENT_RELEASED;
15483     }
15484     else if (id == GAME_CTRL_ID_UNDO ||
15485              id == GAME_CTRL_ID_REDO)
15486     {
15487       button_type = GD_TYPE_NORMAL_BUTTON;
15488       checked = FALSE;
15489       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15490     }
15491     else
15492     {
15493       button_type = GD_TYPE_CHECK_BUTTON;
15494       checked = (gamebutton_info[i].setup_value != NULL ?
15495                  *gamebutton_info[i].setup_value : FALSE);
15496       event_mask = GD_EVENT_PRESSED;
15497     }
15498
15499     gi = CreateGadget(GDI_CUSTOM_ID, id,
15500                       GDI_IMAGE_ID, graphic,
15501                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15502                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15503                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15504                       GDI_WIDTH, gfx->width,
15505                       GDI_HEIGHT, gfx->height,
15506                       GDI_TYPE, button_type,
15507                       GDI_STATE, GD_BUTTON_UNPRESSED,
15508                       GDI_CHECKED, checked,
15509                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15510                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15511                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15512                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15513                       GDI_DIRECT_DRAW, FALSE,
15514                       GDI_EVENT_MASK, event_mask,
15515                       GDI_CALLBACK_ACTION, HandleGameButtons,
15516                       GDI_END);
15517
15518     if (gi == NULL)
15519       Error(ERR_EXIT, "cannot create gadget");
15520
15521     game_gadget[id] = gi;
15522   }
15523 }
15524
15525 void FreeGameButtons(void)
15526 {
15527   int i;
15528
15529   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15530     FreeGadget(game_gadget[i]);
15531 }
15532
15533 static void UnmapGameButtonsAtSamePosition(int id)
15534 {
15535   int i;
15536
15537   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15538     if (i != id &&
15539         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15540         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15541       UnmapGadget(game_gadget[i]);
15542 }
15543
15544 static void UnmapGameButtonsAtSamePosition_All(void)
15545 {
15546   if (setup.show_snapshot_buttons)
15547   {
15548     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15549     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15550     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15551   }
15552   else
15553   {
15554     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15555     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15556     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15557
15558     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15559     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15560     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15561   }
15562 }
15563
15564 static void MapGameButtonsAtSamePosition(int id)
15565 {
15566   int i;
15567
15568   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15569     if (i != id &&
15570         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15571         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15572       MapGadget(game_gadget[i]);
15573
15574   UnmapGameButtonsAtSamePosition_All();
15575 }
15576
15577 void MapUndoRedoButtons(void)
15578 {
15579   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15580   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15581
15582   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15583   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15584
15585   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15586 }
15587
15588 void UnmapUndoRedoButtons(void)
15589 {
15590   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15591   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15592
15593   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15594   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15595
15596   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15597 }
15598
15599 static void MapGameButtonsExt(boolean on_tape)
15600 {
15601   int i;
15602
15603   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15604     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15605         i != GAME_CTRL_ID_UNDO &&
15606         i != GAME_CTRL_ID_REDO)
15607       MapGadget(game_gadget[i]);
15608
15609   UnmapGameButtonsAtSamePosition_All();
15610
15611   RedrawGameButtons();
15612 }
15613
15614 static void UnmapGameButtonsExt(boolean on_tape)
15615 {
15616   int i;
15617
15618   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15619     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15620       UnmapGadget(game_gadget[i]);
15621 }
15622
15623 static void RedrawGameButtonsExt(boolean on_tape)
15624 {
15625   int i;
15626
15627   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15628     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15629       RedrawGadget(game_gadget[i]);
15630
15631   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15632   redraw_mask &= ~REDRAW_ALL;
15633 }
15634
15635 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
15636 {
15637   if (gi == NULL)
15638     return;
15639
15640   gi->checked = state;
15641 }
15642
15643 static void RedrawSoundButtonGadget(int id)
15644 {
15645   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15646              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15647              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15648              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15649              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15650              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15651              id);
15652
15653   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15654   RedrawGadget(game_gadget[id2]);
15655 }
15656
15657 void MapGameButtons(void)
15658 {
15659   MapGameButtonsExt(FALSE);
15660 }
15661
15662 void UnmapGameButtons(void)
15663 {
15664   UnmapGameButtonsExt(FALSE);
15665 }
15666
15667 void RedrawGameButtons(void)
15668 {
15669   RedrawGameButtonsExt(FALSE);
15670 }
15671
15672 void MapGameButtonsOnTape(void)
15673 {
15674   MapGameButtonsExt(TRUE);
15675 }
15676
15677 void UnmapGameButtonsOnTape(void)
15678 {
15679   UnmapGameButtonsExt(TRUE);
15680 }
15681
15682 void RedrawGameButtonsOnTape(void)
15683 {
15684   RedrawGameButtonsExt(TRUE);
15685 }
15686
15687 static void GameUndoRedoExt(void)
15688 {
15689   ClearPlayerAction();
15690
15691   tape.pausing = TRUE;
15692
15693   RedrawPlayfield();
15694   UpdateAndDisplayGameControlValues();
15695
15696   DrawCompleteVideoDisplay();
15697   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15698   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15699   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15700
15701   BackToFront();
15702 }
15703
15704 static void GameUndo(int steps)
15705 {
15706   if (!CheckEngineSnapshotList())
15707     return;
15708
15709   LoadEngineSnapshot_Undo(steps);
15710
15711   GameUndoRedoExt();
15712 }
15713
15714 static void GameRedo(int steps)
15715 {
15716   if (!CheckEngineSnapshotList())
15717     return;
15718
15719   LoadEngineSnapshot_Redo(steps);
15720
15721   GameUndoRedoExt();
15722 }
15723
15724 static void HandleGameButtonsExt(int id, int button)
15725 {
15726   static boolean game_undo_executed = FALSE;
15727   int steps = BUTTON_STEPSIZE(button);
15728   boolean handle_game_buttons =
15729     (game_status == GAME_MODE_PLAYING ||
15730      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15731
15732   if (!handle_game_buttons)
15733     return;
15734
15735   switch (id)
15736   {
15737     case GAME_CTRL_ID_STOP:
15738     case GAME_CTRL_ID_PANEL_STOP:
15739       if (game_status == GAME_MODE_MAIN)
15740         break;
15741
15742       if (tape.playing)
15743         TapeStop();
15744       else
15745         RequestQuitGame(TRUE);
15746
15747       break;
15748
15749     case GAME_CTRL_ID_PAUSE:
15750     case GAME_CTRL_ID_PAUSE2:
15751     case GAME_CTRL_ID_PANEL_PAUSE:
15752       if (network.enabled && game_status == GAME_MODE_PLAYING)
15753       {
15754         if (tape.pausing)
15755           SendToServer_ContinuePlaying();
15756         else
15757           SendToServer_PausePlaying();
15758       }
15759       else
15760         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15761
15762       game_undo_executed = FALSE;
15763
15764       break;
15765
15766     case GAME_CTRL_ID_PLAY:
15767     case GAME_CTRL_ID_PANEL_PLAY:
15768       if (game_status == GAME_MODE_MAIN)
15769       {
15770         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15771       }
15772       else if (tape.pausing)
15773       {
15774         if (network.enabled)
15775           SendToServer_ContinuePlaying();
15776         else
15777           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15778       }
15779       break;
15780
15781     case GAME_CTRL_ID_UNDO:
15782       // Important: When using "save snapshot when collecting an item" mode,
15783       // load last (current) snapshot for first "undo" after pressing "pause"
15784       // (else the last-but-one snapshot would be loaded, because the snapshot
15785       // pointer already points to the last snapshot when pressing "pause",
15786       // which is fine for "every step/move" mode, but not for "every collect")
15787       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15788           !game_undo_executed)
15789         steps--;
15790
15791       game_undo_executed = TRUE;
15792
15793       GameUndo(steps);
15794       break;
15795
15796     case GAME_CTRL_ID_REDO:
15797       GameRedo(steps);
15798       break;
15799
15800     case GAME_CTRL_ID_SAVE:
15801       TapeQuickSave();
15802       break;
15803
15804     case GAME_CTRL_ID_LOAD:
15805       TapeQuickLoad();
15806       break;
15807
15808     case SOUND_CTRL_ID_MUSIC:
15809     case SOUND_CTRL_ID_PANEL_MUSIC:
15810       if (setup.sound_music)
15811       { 
15812         setup.sound_music = FALSE;
15813
15814         FadeMusic();
15815       }
15816       else if (audio.music_available)
15817       { 
15818         setup.sound = setup.sound_music = TRUE;
15819
15820         SetAudioMode(setup.sound);
15821
15822         if (game_status == GAME_MODE_PLAYING)
15823           PlayLevelMusic();
15824       }
15825
15826       RedrawSoundButtonGadget(id);
15827
15828       break;
15829
15830     case SOUND_CTRL_ID_LOOPS:
15831     case SOUND_CTRL_ID_PANEL_LOOPS:
15832       if (setup.sound_loops)
15833         setup.sound_loops = FALSE;
15834       else if (audio.loops_available)
15835       {
15836         setup.sound = setup.sound_loops = TRUE;
15837
15838         SetAudioMode(setup.sound);
15839       }
15840
15841       RedrawSoundButtonGadget(id);
15842
15843       break;
15844
15845     case SOUND_CTRL_ID_SIMPLE:
15846     case SOUND_CTRL_ID_PANEL_SIMPLE:
15847       if (setup.sound_simple)
15848         setup.sound_simple = FALSE;
15849       else if (audio.sound_available)
15850       {
15851         setup.sound = setup.sound_simple = TRUE;
15852
15853         SetAudioMode(setup.sound);
15854       }
15855
15856       RedrawSoundButtonGadget(id);
15857
15858       break;
15859
15860     default:
15861       break;
15862   }
15863 }
15864
15865 static void HandleGameButtons(struct GadgetInfo *gi)
15866 {
15867   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15868 }
15869
15870 void HandleSoundButtonKeys(Key key)
15871 {
15872   if (key == setup.shortcut.sound_simple)
15873     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15874   else if (key == setup.shortcut.sound_loops)
15875     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15876   else if (key == setup.shortcut.sound_music)
15877     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15878 }