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