added message 'level solved' after solving a 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();
1089 static void FadeLevelSoundsAndMusic();
1090
1091 static void HandleGameButtons(struct GadgetInfo *);
1092
1093 int AmoebeNachbarNr(int, int);
1094 void AmoebeUmwandeln(int, int);
1095 void ContinueMoving(int, int);
1096 void Bang(int, int);
1097 void InitMovDir(int, int);
1098 void InitAmoebaNr(int, int);
1099 int NewHiScore(void);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!options.network || player->connected)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 void InitGame()
3277 {
3278   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3279   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3280   int fade_mask = REDRAW_FIELD;
3281
3282   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3283   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3284   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3285   int initial_move_dir = MV_DOWN;
3286   int i, j, x, y;
3287
3288   // required here to update video display before fading (FIX THIS)
3289   DrawMaskedBorder(REDRAW_DOOR_2);
3290
3291   if (!game.restart_level)
3292     CloseDoor(DOOR_CLOSE_1);
3293
3294   SetGameStatus(GAME_MODE_PLAYING);
3295
3296   if (level_editor_test_game)
3297     FadeSkipNextFadeIn();
3298   else
3299     FadeSetEnterScreen();
3300
3301   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3302     fade_mask = REDRAW_ALL;
3303
3304   FadeLevelSoundsAndMusic();
3305
3306   ExpireSoundLoops(TRUE);
3307
3308   FadeOut(fade_mask);
3309
3310   /* needed if different viewport properties defined for playing */
3311   ChangeViewportPropertiesIfNeeded();
3312
3313   ClearField();
3314
3315   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3316
3317   DrawCompleteVideoDisplay();
3318
3319   InitGameEngine();
3320   InitGameControlValues();
3321
3322   /* don't play tapes over network */
3323   network_playing = (options.network && !tape.playing);
3324
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326   {
3327     struct PlayerInfo *player = &stored_player[i];
3328
3329     player->index_nr = i;
3330     player->index_bit = (1 << i);
3331     player->element_nr = EL_PLAYER_1 + i;
3332
3333     player->present = FALSE;
3334     player->active = FALSE;
3335     player->mapped = FALSE;
3336
3337     player->killed = FALSE;
3338     player->reanimated = FALSE;
3339
3340     player->action = 0;
3341     player->effective_action = 0;
3342     player->programmed_action = 0;
3343
3344     player->mouse_action.lx = 0;
3345     player->mouse_action.ly = 0;
3346     player->mouse_action.button = 0;
3347
3348     player->effective_mouse_action.lx = 0;
3349     player->effective_mouse_action.ly = 0;
3350     player->effective_mouse_action.button = 0;
3351
3352     player->score = 0;
3353     player->score_final = 0;
3354
3355     player->health = MAX_HEALTH;
3356     player->health_final = MAX_HEALTH;
3357
3358     player->gems_still_needed = level.gems_needed;
3359     player->sokobanfields_still_needed = 0;
3360     player->lights_still_needed = 0;
3361     player->friends_still_needed = 0;
3362
3363     for (j = 0; j < MAX_NUM_KEYS; j++)
3364       player->key[j] = FALSE;
3365
3366     player->num_white_keys = 0;
3367
3368     player->dynabomb_count = 0;
3369     player->dynabomb_size = 1;
3370     player->dynabombs_left = 0;
3371     player->dynabomb_xl = FALSE;
3372
3373     player->MovDir = initial_move_dir;
3374     player->MovPos = 0;
3375     player->GfxPos = 0;
3376     player->GfxDir = initial_move_dir;
3377     player->GfxAction = ACTION_DEFAULT;
3378     player->Frame = 0;
3379     player->StepFrame = 0;
3380
3381     player->initial_element = player->element_nr;
3382     player->artwork_element =
3383       (level.use_artwork_element[i] ? level.artwork_element[i] :
3384        player->element_nr);
3385     player->use_murphy = FALSE;
3386
3387     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3388     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3389
3390     player->gravity = level.initial_player_gravity[i];
3391
3392     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3393
3394     player->actual_frame_counter = 0;
3395
3396     player->step_counter = 0;
3397
3398     player->last_move_dir = initial_move_dir;
3399
3400     player->is_active = FALSE;
3401
3402     player->is_waiting = FALSE;
3403     player->is_moving = FALSE;
3404     player->is_auto_moving = FALSE;
3405     player->is_digging = FALSE;
3406     player->is_snapping = FALSE;
3407     player->is_collecting = FALSE;
3408     player->is_pushing = FALSE;
3409     player->is_switching = FALSE;
3410     player->is_dropping = FALSE;
3411     player->is_dropping_pressed = FALSE;
3412
3413     player->is_bored = FALSE;
3414     player->is_sleeping = FALSE;
3415
3416     player->was_waiting = TRUE;
3417     player->was_moving = FALSE;
3418     player->was_snapping = FALSE;
3419     player->was_dropping = FALSE;
3420
3421     player->force_dropping = FALSE;
3422
3423     player->frame_counter_bored = -1;
3424     player->frame_counter_sleeping = -1;
3425
3426     player->anim_delay_counter = 0;
3427     player->post_delay_counter = 0;
3428
3429     player->dir_waiting = initial_move_dir;
3430     player->action_waiting = ACTION_DEFAULT;
3431     player->last_action_waiting = ACTION_DEFAULT;
3432     player->special_action_bored = ACTION_DEFAULT;
3433     player->special_action_sleeping = ACTION_DEFAULT;
3434
3435     player->switch_x = -1;
3436     player->switch_y = -1;
3437
3438     player->drop_x = -1;
3439     player->drop_y = -1;
3440
3441     player->show_envelope = 0;
3442
3443     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3444
3445     player->push_delay       = -1;      /* initialized when pushing starts */
3446     player->push_delay_value = game.initial_push_delay_value;
3447
3448     player->drop_delay = 0;
3449     player->drop_pressed_delay = 0;
3450
3451     player->last_jx = -1;
3452     player->last_jy = -1;
3453     player->jx = -1;
3454     player->jy = -1;
3455
3456     player->shield_normal_time_left = 0;
3457     player->shield_deadly_time_left = 0;
3458
3459     player->inventory_infinite_element = EL_UNDEFINED;
3460     player->inventory_size = 0;
3461
3462     if (level.use_initial_inventory[i])
3463     {
3464       for (j = 0; j < level.initial_inventory_size[i]; j++)
3465       {
3466         int element = level.initial_inventory_content[i][j];
3467         int collect_count = element_info[element].collect_count_initial;
3468         int k;
3469
3470         if (!IS_CUSTOM_ELEMENT(element))
3471           collect_count = 1;
3472
3473         if (collect_count == 0)
3474           player->inventory_infinite_element = element;
3475         else
3476           for (k = 0; k < collect_count; k++)
3477             if (player->inventory_size < MAX_INVENTORY_SIZE)
3478               player->inventory_element[player->inventory_size++] = element;
3479       }
3480     }
3481
3482     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3483     SnapField(player, 0, 0);
3484
3485     player->LevelSolved = FALSE;
3486     player->GameOver = FALSE;
3487
3488     player->LevelSolved_GameWon = FALSE;
3489     player->LevelSolved_GameEnd = FALSE;
3490     player->LevelSolved_PanelOff = FALSE;
3491     player->LevelSolved_SaveTape = FALSE;
3492     player->LevelSolved_SaveScore = FALSE;
3493
3494     player->LevelSolved_CountingTime = 0;
3495     player->LevelSolved_CountingScore = 0;
3496     player->LevelSolved_CountingHealth = 0;
3497
3498     map_player_action[i] = i;
3499   }
3500
3501   network_player_action_received = FALSE;
3502
3503 #if defined(NETWORK_AVALIABLE)
3504   /* initial null action */
3505   if (network_playing)
3506     SendToServer_MovePlayer(MV_NONE);
3507 #endif
3508
3509   ZX = ZY = -1;
3510   ExitX = ExitY = -1;
3511
3512   FrameCounter = 0;
3513   TimeFrames = 0;
3514   TimePlayed = 0;
3515   TimeLeft = level.time;
3516   TapeTime = 0;
3517
3518   ScreenMovDir = MV_NONE;
3519   ScreenMovPos = 0;
3520   ScreenGfxPos = 0;
3521
3522   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3523
3524   AllPlayersGone = FALSE;
3525
3526   game.no_time_limit = (level.time == 0);
3527
3528   game.yamyam_content_nr = 0;
3529   game.robot_wheel_active = FALSE;
3530   game.magic_wall_active = FALSE;
3531   game.magic_wall_time_left = 0;
3532   game.light_time_left = 0;
3533   game.timegate_time_left = 0;
3534   game.switchgate_pos = 0;
3535   game.wind_direction = level.wind_direction_initial;
3536
3537   game.lenses_time_left = 0;
3538   game.magnify_time_left = 0;
3539
3540   game.ball_state = level.ball_state_initial;
3541   game.ball_content_nr = 0;
3542
3543   game.envelope_active = FALSE;
3544
3545   /* set focus to local player for network games, else to all players */
3546   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3547   game.centered_player_nr_next = game.centered_player_nr;
3548   game.set_centered_player = FALSE;
3549
3550   if (network_playing && tape.recording)
3551   {
3552     /* store client dependent player focus when recording network games */
3553     tape.centered_player_nr_next = game.centered_player_nr_next;
3554     tape.set_centered_player = TRUE;
3555   }
3556
3557   for (i = 0; i < NUM_BELTS; i++)
3558   {
3559     game.belt_dir[i] = MV_NONE;
3560     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3561   }
3562
3563   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3564     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3565
3566 #if DEBUG_INIT_PLAYER
3567   if (options.debug)
3568   {
3569     printf("Player status at level initialization:\n");
3570   }
3571 #endif
3572
3573   SCAN_PLAYFIELD(x, y)
3574   {
3575     Feld[x][y] = level.field[x][y];
3576     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3577     ChangeDelay[x][y] = 0;
3578     ChangePage[x][y] = -1;
3579     CustomValue[x][y] = 0;              /* initialized in InitField() */
3580     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3581     AmoebaNr[x][y] = 0;
3582     WasJustMoving[x][y] = 0;
3583     WasJustFalling[x][y] = 0;
3584     CheckCollision[x][y] = 0;
3585     CheckImpact[x][y] = 0;
3586     Stop[x][y] = FALSE;
3587     Pushed[x][y] = FALSE;
3588
3589     ChangeCount[x][y] = 0;
3590     ChangeEvent[x][y] = -1;
3591
3592     ExplodePhase[x][y] = 0;
3593     ExplodeDelay[x][y] = 0;
3594     ExplodeField[x][y] = EX_TYPE_NONE;
3595
3596     RunnerVisit[x][y] = 0;
3597     PlayerVisit[x][y] = 0;
3598
3599     GfxFrame[x][y] = 0;
3600     GfxRandom[x][y] = INIT_GFX_RANDOM();
3601     GfxElement[x][y] = EL_UNDEFINED;
3602     GfxAction[x][y] = ACTION_DEFAULT;
3603     GfxDir[x][y] = MV_NONE;
3604     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3605   }
3606
3607   SCAN_PLAYFIELD(x, y)
3608   {
3609     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3610       emulate_bd = FALSE;
3611     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3612       emulate_sb = FALSE;
3613     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3614       emulate_sp = FALSE;
3615
3616     InitField(x, y, TRUE);
3617
3618     ResetGfxAnimation(x, y);
3619   }
3620
3621   InitBeltMovement();
3622
3623   for (i = 0; i < MAX_PLAYERS; i++)
3624   {
3625     struct PlayerInfo *player = &stored_player[i];
3626
3627     /* set number of special actions for bored and sleeping animation */
3628     player->num_special_action_bored =
3629       get_num_special_action(player->artwork_element,
3630                              ACTION_BORING_1, ACTION_BORING_LAST);
3631     player->num_special_action_sleeping =
3632       get_num_special_action(player->artwork_element,
3633                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3634   }
3635
3636   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3637                     emulate_sb ? EMU_SOKOBAN :
3638                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3639
3640   /* initialize type of slippery elements */
3641   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3642   {
3643     if (!IS_CUSTOM_ELEMENT(i))
3644     {
3645       /* default: elements slip down either to the left or right randomly */
3646       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3647
3648       /* SP style elements prefer to slip down on the left side */
3649       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3650         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3651
3652       /* BD style elements prefer to slip down on the left side */
3653       if (game.emulation == EMU_BOULDERDASH)
3654         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3655     }
3656   }
3657
3658   /* initialize explosion and ignition delay */
3659   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3660   {
3661     if (!IS_CUSTOM_ELEMENT(i))
3662     {
3663       int num_phase = 8;
3664       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3665                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3666                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3667       int last_phase = (num_phase + 1) * delay;
3668       int half_phase = (num_phase / 2) * delay;
3669
3670       element_info[i].explosion_delay = last_phase - 1;
3671       element_info[i].ignition_delay = half_phase;
3672
3673       if (i == EL_BLACK_ORB)
3674         element_info[i].ignition_delay = 1;
3675     }
3676   }
3677
3678   /* correct non-moving belts to start moving left */
3679   for (i = 0; i < NUM_BELTS; i++)
3680     if (game.belt_dir[i] == MV_NONE)
3681       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3682
3683 #if USE_NEW_PLAYER_ASSIGNMENTS
3684   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3685   /* choose default local player */
3686   local_player = &stored_player[0];
3687
3688   for (i = 0; i < MAX_PLAYERS; i++)
3689     stored_player[i].connected = FALSE;
3690
3691   local_player->connected = TRUE;
3692   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3693
3694   if (tape.playing)
3695   {
3696     for (i = 0; i < MAX_PLAYERS; i++)
3697       stored_player[i].connected = tape.player_participates[i];
3698   }
3699   else if (game.team_mode && !options.network)
3700   {
3701     /* try to guess locally connected team mode players (needed for correct
3702        assignment of player figures from level to locally playing players) */
3703
3704     for (i = 0; i < MAX_PLAYERS; i++)
3705       if (setup.input[i].use_joystick ||
3706           setup.input[i].key.left != KSYM_UNDEFINED)
3707         stored_player[i].connected = TRUE;
3708   }
3709
3710 #if DEBUG_INIT_PLAYER
3711   if (options.debug)
3712   {
3713     printf("Player status after level initialization:\n");
3714
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716     {
3717       struct PlayerInfo *player = &stored_player[i];
3718
3719       printf("- player %d: present == %d, connected == %d, active == %d",
3720              i + 1,
3721              player->present,
3722              player->connected,
3723              player->active);
3724
3725       if (local_player == player)
3726         printf(" (local player)");
3727
3728       printf("\n");
3729     }
3730   }
3731 #endif
3732
3733 #if DEBUG_INIT_PLAYER
3734   if (options.debug)
3735     printf("Reassigning players ...\n");
3736 #endif
3737
3738   /* check if any connected player was not found in playfield */
3739   for (i = 0; i < MAX_PLAYERS; i++)
3740   {
3741     struct PlayerInfo *player = &stored_player[i];
3742
3743     if (player->connected && !player->present)
3744     {
3745       struct PlayerInfo *field_player = NULL;
3746
3747 #if DEBUG_INIT_PLAYER
3748       if (options.debug)
3749         printf("- looking for field player for player %d ...\n", i + 1);
3750 #endif
3751
3752       /* assign first free player found that is present in the playfield */
3753
3754       /* first try: look for unmapped playfield player that is not connected */
3755       for (j = 0; j < MAX_PLAYERS; j++)
3756         if (field_player == NULL &&
3757             stored_player[j].present &&
3758             !stored_player[j].mapped &&
3759             !stored_player[j].connected)
3760           field_player = &stored_player[j];
3761
3762       /* second try: look for *any* unmapped playfield player */
3763       for (j = 0; j < MAX_PLAYERS; j++)
3764         if (field_player == NULL &&
3765             stored_player[j].present &&
3766             !stored_player[j].mapped)
3767           field_player = &stored_player[j];
3768
3769       if (field_player != NULL)
3770       {
3771         int jx = field_player->jx, jy = field_player->jy;
3772
3773 #if DEBUG_INIT_PLAYER
3774         if (options.debug)
3775           printf("- found player %d\n", field_player->index_nr + 1);
3776 #endif
3777
3778         player->present = FALSE;
3779         player->active = FALSE;
3780
3781         field_player->present = TRUE;
3782         field_player->active = TRUE;
3783
3784         /*
3785         player->initial_element = field_player->initial_element;
3786         player->artwork_element = field_player->artwork_element;
3787
3788         player->block_last_field       = field_player->block_last_field;
3789         player->block_delay_adjustment = field_player->block_delay_adjustment;
3790         */
3791
3792         StorePlayer[jx][jy] = field_player->element_nr;
3793
3794         field_player->jx = field_player->last_jx = jx;
3795         field_player->jy = field_player->last_jy = jy;
3796
3797         if (local_player == player)
3798           local_player = field_player;
3799
3800         map_player_action[field_player->index_nr] = i;
3801
3802         field_player->mapped = TRUE;
3803
3804 #if DEBUG_INIT_PLAYER
3805         if (options.debug)
3806           printf("- map_player_action[%d] == %d\n",
3807                  field_player->index_nr + 1, i + 1);
3808 #endif
3809       }
3810     }
3811
3812     if (player->connected && player->present)
3813       player->mapped = TRUE;
3814   }
3815
3816 #if DEBUG_INIT_PLAYER
3817   if (options.debug)
3818   {
3819     printf("Player status after player assignment (first stage):\n");
3820
3821     for (i = 0; i < MAX_PLAYERS; i++)
3822     {
3823       struct PlayerInfo *player = &stored_player[i];
3824
3825       printf("- player %d: present == %d, connected == %d, active == %d",
3826              i + 1,
3827              player->present,
3828              player->connected,
3829              player->active);
3830
3831       if (local_player == player)
3832         printf(" (local player)");
3833
3834       printf("\n");
3835     }
3836   }
3837 #endif
3838
3839 #else
3840
3841   /* check if any connected player was not found in playfield */
3842   for (i = 0; i < MAX_PLAYERS; i++)
3843   {
3844     struct PlayerInfo *player = &stored_player[i];
3845
3846     if (player->connected && !player->present)
3847     {
3848       for (j = 0; j < MAX_PLAYERS; j++)
3849       {
3850         struct PlayerInfo *field_player = &stored_player[j];
3851         int jx = field_player->jx, jy = field_player->jy;
3852
3853         /* assign first free player found that is present in the playfield */
3854         if (field_player->present && !field_player->connected)
3855         {
3856           player->present = TRUE;
3857           player->active = TRUE;
3858
3859           field_player->present = FALSE;
3860           field_player->active = FALSE;
3861
3862           player->initial_element = field_player->initial_element;
3863           player->artwork_element = field_player->artwork_element;
3864
3865           player->block_last_field       = field_player->block_last_field;
3866           player->block_delay_adjustment = field_player->block_delay_adjustment;
3867
3868           StorePlayer[jx][jy] = player->element_nr;
3869
3870           player->jx = player->last_jx = jx;
3871           player->jy = player->last_jy = jy;
3872
3873           break;
3874         }
3875       }
3876     }
3877   }
3878 #endif
3879
3880 #if 0
3881   printf("::: local_player->present == %d\n", local_player->present);
3882 #endif
3883
3884   if (tape.playing)
3885   {
3886     /* when playing a tape, eliminate all players who do not participate */
3887
3888 #if USE_NEW_PLAYER_ASSIGNMENTS
3889
3890     if (!game.team_mode)
3891     {
3892       for (i = 0; i < MAX_PLAYERS; i++)
3893       {
3894         if (stored_player[i].active &&
3895             !tape.player_participates[map_player_action[i]])
3896         {
3897           struct PlayerInfo *player = &stored_player[i];
3898           int jx = player->jx, jy = player->jy;
3899
3900 #if DEBUG_INIT_PLAYER
3901           if (options.debug)
3902             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3903 #endif
3904
3905           player->active = FALSE;
3906           StorePlayer[jx][jy] = 0;
3907           Feld[jx][jy] = EL_EMPTY;
3908         }
3909       }
3910     }
3911
3912 #else
3913
3914     for (i = 0; i < MAX_PLAYERS; i++)
3915     {
3916       if (stored_player[i].active &&
3917           !tape.player_participates[i])
3918       {
3919         struct PlayerInfo *player = &stored_player[i];
3920         int jx = player->jx, jy = player->jy;
3921
3922         player->active = FALSE;
3923         StorePlayer[jx][jy] = 0;
3924         Feld[jx][jy] = EL_EMPTY;
3925       }
3926     }
3927 #endif
3928   }
3929   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3930   {
3931     /* when in single player mode, eliminate all but the first active player */
3932
3933     for (i = 0; i < MAX_PLAYERS; i++)
3934     {
3935       if (stored_player[i].active)
3936       {
3937         for (j = i + 1; j < MAX_PLAYERS; j++)
3938         {
3939           if (stored_player[j].active)
3940           {
3941             struct PlayerInfo *player = &stored_player[j];
3942             int jx = player->jx, jy = player->jy;
3943
3944             player->active = FALSE;
3945             player->present = FALSE;
3946
3947             StorePlayer[jx][jy] = 0;
3948             Feld[jx][jy] = EL_EMPTY;
3949           }
3950         }
3951       }
3952     }
3953   }
3954
3955   /* when recording the game, store which players take part in the game */
3956   if (tape.recording)
3957   {
3958 #if USE_NEW_PLAYER_ASSIGNMENTS
3959     for (i = 0; i < MAX_PLAYERS; i++)
3960       if (stored_player[i].connected)
3961         tape.player_participates[i] = TRUE;
3962 #else
3963     for (i = 0; i < MAX_PLAYERS; i++)
3964       if (stored_player[i].active)
3965         tape.player_participates[i] = TRUE;
3966 #endif
3967   }
3968
3969 #if DEBUG_INIT_PLAYER
3970   if (options.debug)
3971   {
3972     printf("Player status after player assignment (final stage):\n");
3973
3974     for (i = 0; i < MAX_PLAYERS; i++)
3975     {
3976       struct PlayerInfo *player = &stored_player[i];
3977
3978       printf("- player %d: present == %d, connected == %d, active == %d",
3979              i + 1,
3980              player->present,
3981              player->connected,
3982              player->active);
3983
3984       if (local_player == player)
3985         printf(" (local player)");
3986
3987       printf("\n");
3988     }
3989   }
3990 #endif
3991
3992   if (BorderElement == EL_EMPTY)
3993   {
3994     SBX_Left = 0;
3995     SBX_Right = lev_fieldx - SCR_FIELDX;
3996     SBY_Upper = 0;
3997     SBY_Lower = lev_fieldy - SCR_FIELDY;
3998   }
3999   else
4000   {
4001     SBX_Left = -1;
4002     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4003     SBY_Upper = -1;
4004     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4005   }
4006
4007   if (full_lev_fieldx <= SCR_FIELDX)
4008     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4009   if (full_lev_fieldy <= SCR_FIELDY)
4010     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4011
4012   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4013     SBX_Left--;
4014   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4015     SBY_Upper--;
4016
4017   /* if local player not found, look for custom element that might create
4018      the player (make some assumptions about the right custom element) */
4019   if (!local_player->present)
4020   {
4021     int start_x = 0, start_y = 0;
4022     int found_rating = 0;
4023     int found_element = EL_UNDEFINED;
4024     int player_nr = local_player->index_nr;
4025
4026     SCAN_PLAYFIELD(x, y)
4027     {
4028       int element = Feld[x][y];
4029       int content;
4030       int xx, yy;
4031       boolean is_player;
4032
4033       if (level.use_start_element[player_nr] &&
4034           level.start_element[player_nr] == element &&
4035           found_rating < 4)
4036       {
4037         start_x = x;
4038         start_y = y;
4039
4040         found_rating = 4;
4041         found_element = element;
4042       }
4043
4044       if (!IS_CUSTOM_ELEMENT(element))
4045         continue;
4046
4047       if (CAN_CHANGE(element))
4048       {
4049         for (i = 0; i < element_info[element].num_change_pages; i++)
4050         {
4051           /* check for player created from custom element as single target */
4052           content = element_info[element].change_page[i].target_element;
4053           is_player = ELEM_IS_PLAYER(content);
4054
4055           if (is_player && (found_rating < 3 ||
4056                             (found_rating == 3 && element < found_element)))
4057           {
4058             start_x = x;
4059             start_y = y;
4060
4061             found_rating = 3;
4062             found_element = element;
4063           }
4064         }
4065       }
4066
4067       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4068       {
4069         /* check for player created from custom element as explosion content */
4070         content = element_info[element].content.e[xx][yy];
4071         is_player = ELEM_IS_PLAYER(content);
4072
4073         if (is_player && (found_rating < 2 ||
4074                           (found_rating == 2 && element < found_element)))
4075         {
4076           start_x = x + xx - 1;
4077           start_y = y + yy - 1;
4078
4079           found_rating = 2;
4080           found_element = element;
4081         }
4082
4083         if (!CAN_CHANGE(element))
4084           continue;
4085
4086         for (i = 0; i < element_info[element].num_change_pages; i++)
4087         {
4088           /* check for player created from custom element as extended target */
4089           content =
4090             element_info[element].change_page[i].target_content.e[xx][yy];
4091
4092           is_player = ELEM_IS_PLAYER(content);
4093
4094           if (is_player && (found_rating < 1 ||
4095                             (found_rating == 1 && element < found_element)))
4096           {
4097             start_x = x + xx - 1;
4098             start_y = y + yy - 1;
4099
4100             found_rating = 1;
4101             found_element = element;
4102           }
4103         }
4104       }
4105     }
4106
4107     scroll_x = SCROLL_POSITION_X(start_x);
4108     scroll_y = SCROLL_POSITION_Y(start_y);
4109   }
4110   else
4111   {
4112     scroll_x = SCROLL_POSITION_X(local_player->jx);
4113     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4114   }
4115
4116   /* !!! FIX THIS (START) !!! */
4117   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4118   {
4119     InitGameEngine_EM();
4120   }
4121   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4122   {
4123     InitGameEngine_SP();
4124   }
4125   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4126   {
4127     InitGameEngine_MM();
4128   }
4129   else
4130   {
4131     DrawLevel(REDRAW_FIELD);
4132     DrawAllPlayers();
4133
4134     /* after drawing the level, correct some elements */
4135     if (game.timegate_time_left == 0)
4136       CloseAllOpenTimegates();
4137   }
4138
4139   /* blit playfield from scroll buffer to normal back buffer for fading in */
4140   BlitScreenToBitmap(backbuffer);
4141   /* !!! FIX THIS (END) !!! */
4142
4143   DrawMaskedBorder(fade_mask);
4144
4145   FadeIn(fade_mask);
4146
4147 #if 1
4148   // full screen redraw is required at this point in the following cases:
4149   // - special editor door undrawn when game was started from level editor
4150   // - drawing area (playfield) was changed and has to be removed completely
4151   redraw_mask = REDRAW_ALL;
4152   BackToFront();
4153 #endif
4154
4155   if (!game.restart_level)
4156   {
4157     /* copy default game door content to main double buffer */
4158
4159     /* !!! CHECK AGAIN !!! */
4160     SetPanelBackground();
4161     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4162     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4163   }
4164
4165   SetPanelBackground();
4166   SetDrawBackgroundMask(REDRAW_DOOR_1);
4167
4168   UpdateAndDisplayGameControlValues();
4169
4170   if (!game.restart_level)
4171   {
4172     UnmapGameButtons();
4173     UnmapTapeButtons();
4174
4175     FreeGameButtons();
4176     CreateGameButtons();
4177
4178     MapGameButtons();
4179     MapTapeButtons();
4180
4181     /* copy actual game door content to door double buffer for OpenDoor() */
4182     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4183
4184     OpenDoor(DOOR_OPEN_ALL);
4185
4186     PlaySound(SND_GAME_STARTING);
4187
4188     if (setup.sound_music)
4189       PlayLevelMusic();
4190
4191     KeyboardAutoRepeatOffUnlessAutoplay();
4192
4193 #if DEBUG_INIT_PLAYER
4194     if (options.debug)
4195     {
4196       printf("Player status (final):\n");
4197
4198       for (i = 0; i < MAX_PLAYERS; i++)
4199       {
4200         struct PlayerInfo *player = &stored_player[i];
4201
4202         printf("- player %d: present == %d, connected == %d, active == %d",
4203                i + 1,
4204                player->present,
4205                player->connected,
4206                player->active);
4207
4208         if (local_player == player)
4209           printf(" (local player)");
4210
4211         printf("\n");
4212       }
4213     }
4214 #endif
4215   }
4216
4217   UnmapAllGadgets();
4218
4219   MapGameButtons();
4220   MapTapeButtons();
4221
4222   if (!game.restart_level && !tape.playing)
4223   {
4224     LevelStats_incPlayed(level_nr);
4225
4226     SaveLevelSetup_SeriesInfo();
4227   }
4228
4229   game.restart_level = FALSE;
4230   game.restart_game_message = NULL;
4231
4232   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4233     InitGameActions_MM();
4234
4235   SaveEngineSnapshotToListInitial();
4236 }
4237
4238 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4239                         int actual_player_x, int actual_player_y)
4240 {
4241   /* this is used for non-R'n'D game engines to update certain engine values */
4242
4243   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4244   {
4245     actual_player_x = correctLevelPosX_EM(actual_player_x);
4246     actual_player_y = correctLevelPosY_EM(actual_player_y);
4247   }
4248
4249   /* needed to determine if sounds are played within the visible screen area */
4250   scroll_x = actual_scroll_x;
4251   scroll_y = actual_scroll_y;
4252
4253   /* needed to get player position for "follow finger" playing input method */
4254   local_player->jx = actual_player_x;
4255   local_player->jy = actual_player_y;
4256 }
4257
4258 void InitMovDir(int x, int y)
4259 {
4260   int i, element = Feld[x][y];
4261   static int xy[4][2] =
4262   {
4263     {  0, +1 },
4264     { +1,  0 },
4265     {  0, -1 },
4266     { -1,  0 }
4267   };
4268   static int direction[3][4] =
4269   {
4270     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4271     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4272     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4273   };
4274
4275   switch (element)
4276   {
4277     case EL_BUG_RIGHT:
4278     case EL_BUG_UP:
4279     case EL_BUG_LEFT:
4280     case EL_BUG_DOWN:
4281       Feld[x][y] = EL_BUG;
4282       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4283       break;
4284
4285     case EL_SPACESHIP_RIGHT:
4286     case EL_SPACESHIP_UP:
4287     case EL_SPACESHIP_LEFT:
4288     case EL_SPACESHIP_DOWN:
4289       Feld[x][y] = EL_SPACESHIP;
4290       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4291       break;
4292
4293     case EL_BD_BUTTERFLY_RIGHT:
4294     case EL_BD_BUTTERFLY_UP:
4295     case EL_BD_BUTTERFLY_LEFT:
4296     case EL_BD_BUTTERFLY_DOWN:
4297       Feld[x][y] = EL_BD_BUTTERFLY;
4298       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4299       break;
4300
4301     case EL_BD_FIREFLY_RIGHT:
4302     case EL_BD_FIREFLY_UP:
4303     case EL_BD_FIREFLY_LEFT:
4304     case EL_BD_FIREFLY_DOWN:
4305       Feld[x][y] = EL_BD_FIREFLY;
4306       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4307       break;
4308
4309     case EL_PACMAN_RIGHT:
4310     case EL_PACMAN_UP:
4311     case EL_PACMAN_LEFT:
4312     case EL_PACMAN_DOWN:
4313       Feld[x][y] = EL_PACMAN;
4314       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4315       break;
4316
4317     case EL_YAMYAM_LEFT:
4318     case EL_YAMYAM_RIGHT:
4319     case EL_YAMYAM_UP:
4320     case EL_YAMYAM_DOWN:
4321       Feld[x][y] = EL_YAMYAM;
4322       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4323       break;
4324
4325     case EL_SP_SNIKSNAK:
4326       MovDir[x][y] = MV_UP;
4327       break;
4328
4329     case EL_SP_ELECTRON:
4330       MovDir[x][y] = MV_LEFT;
4331       break;
4332
4333     case EL_MOLE_LEFT:
4334     case EL_MOLE_RIGHT:
4335     case EL_MOLE_UP:
4336     case EL_MOLE_DOWN:
4337       Feld[x][y] = EL_MOLE;
4338       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4339       break;
4340
4341     default:
4342       if (IS_CUSTOM_ELEMENT(element))
4343       {
4344         struct ElementInfo *ei = &element_info[element];
4345         int move_direction_initial = ei->move_direction_initial;
4346         int move_pattern = ei->move_pattern;
4347
4348         if (move_direction_initial == MV_START_PREVIOUS)
4349         {
4350           if (MovDir[x][y] != MV_NONE)
4351             return;
4352
4353           move_direction_initial = MV_START_AUTOMATIC;
4354         }
4355
4356         if (move_direction_initial == MV_START_RANDOM)
4357           MovDir[x][y] = 1 << RND(4);
4358         else if (move_direction_initial & MV_ANY_DIRECTION)
4359           MovDir[x][y] = move_direction_initial;
4360         else if (move_pattern == MV_ALL_DIRECTIONS ||
4361                  move_pattern == MV_TURNING_LEFT ||
4362                  move_pattern == MV_TURNING_RIGHT ||
4363                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4364                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4365                  move_pattern == MV_TURNING_RANDOM)
4366           MovDir[x][y] = 1 << RND(4);
4367         else if (move_pattern == MV_HORIZONTAL)
4368           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4369         else if (move_pattern == MV_VERTICAL)
4370           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4371         else if (move_pattern & MV_ANY_DIRECTION)
4372           MovDir[x][y] = element_info[element].move_pattern;
4373         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4374                  move_pattern == MV_ALONG_RIGHT_SIDE)
4375         {
4376           /* use random direction as default start direction */
4377           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4378             MovDir[x][y] = 1 << RND(4);
4379
4380           for (i = 0; i < NUM_DIRECTIONS; i++)
4381           {
4382             int x1 = x + xy[i][0];
4383             int y1 = y + xy[i][1];
4384
4385             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4386             {
4387               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4388                 MovDir[x][y] = direction[0][i];
4389               else
4390                 MovDir[x][y] = direction[1][i];
4391
4392               break;
4393             }
4394           }
4395         }                
4396       }
4397       else
4398       {
4399         MovDir[x][y] = 1 << RND(4);
4400
4401         if (element != EL_BUG &&
4402             element != EL_SPACESHIP &&
4403             element != EL_BD_BUTTERFLY &&
4404             element != EL_BD_FIREFLY)
4405           break;
4406
4407         for (i = 0; i < NUM_DIRECTIONS; i++)
4408         {
4409           int x1 = x + xy[i][0];
4410           int y1 = y + xy[i][1];
4411
4412           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4413           {
4414             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4415             {
4416               MovDir[x][y] = direction[0][i];
4417               break;
4418             }
4419             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4420                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4421             {
4422               MovDir[x][y] = direction[1][i];
4423               break;
4424             }
4425           }
4426         }
4427       }
4428       break;
4429   }
4430
4431   GfxDir[x][y] = MovDir[x][y];
4432 }
4433
4434 void InitAmoebaNr(int x, int y)
4435 {
4436   int i;
4437   int group_nr = AmoebeNachbarNr(x, y);
4438
4439   if (group_nr == 0)
4440   {
4441     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4442     {
4443       if (AmoebaCnt[i] == 0)
4444       {
4445         group_nr = i;
4446         break;
4447       }
4448     }
4449   }
4450
4451   AmoebaNr[x][y] = group_nr;
4452   AmoebaCnt[group_nr]++;
4453   AmoebaCnt2[group_nr]++;
4454 }
4455
4456 static void PlayerWins(struct PlayerInfo *player)
4457 {
4458   player->LevelSolved = TRUE;
4459   player->GameOver = TRUE;
4460
4461   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4462                          level.native_em_level->lev->score :
4463                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4464                          game_mm.score :
4465                          player->score);
4466   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4467                           MM_HEALTH(game_mm.laser_overload_value) :
4468                           player->health);
4469
4470   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4471                                       TimeLeft);
4472   player->LevelSolved_CountingScore = player->score_final;
4473   player->LevelSolved_CountingHealth = player->health_final;
4474 }
4475
4476 void GameWon()
4477 {
4478   static int time_count_steps;
4479   static int time, time_final;
4480   static int score, score_final;
4481   static int health, health_final;
4482   static int game_over_delay_1 = 0;
4483   static int game_over_delay_2 = 0;
4484   static int game_over_delay_3 = 0;
4485   int game_over_delay_value_1 = 50;
4486   int game_over_delay_value_2 = 25;
4487   int game_over_delay_value_3 = 50;
4488
4489   if (!local_player->LevelSolved_GameWon)
4490   {
4491     int i;
4492
4493     /* do not start end game actions before the player stops moving (to exit) */
4494     if (local_player->MovPos)
4495       return;
4496
4497     local_player->LevelSolved_GameWon = TRUE;
4498     local_player->LevelSolved_SaveTape = tape.recording;
4499     local_player->LevelSolved_SaveScore = !tape.playing;
4500
4501     if (!tape.playing)
4502     {
4503       LevelStats_incSolved(level_nr);
4504
4505       SaveLevelSetup_SeriesInfo();
4506     }
4507
4508     if (tape.auto_play)         /* tape might already be stopped here */
4509       tape.auto_play_level_solved = TRUE;
4510
4511     TapeStop();
4512
4513     game_over_delay_1 = 0;
4514     game_over_delay_2 = 0;
4515     game_over_delay_3 = game_over_delay_value_3;
4516
4517     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4518     score = score_final = local_player->score_final;
4519     health = health_final = local_player->health_final;
4520
4521     if (level.score[SC_TIME_BONUS] > 0)
4522     {
4523       if (TimeLeft > 0)
4524       {
4525         time_final = 0;
4526         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4527       }
4528       else if (game.no_time_limit && TimePlayed < 999)
4529       {
4530         time_final = 999;
4531         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4532       }
4533
4534       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4535
4536       game_over_delay_1 = game_over_delay_value_1;
4537
4538       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4539       {
4540         health_final = 0;
4541         score_final += health * level.score[SC_TIME_BONUS];
4542
4543         game_over_delay_2 = game_over_delay_value_2;
4544       }
4545
4546       local_player->score_final = score_final;
4547       local_player->health_final = health_final;
4548     }
4549
4550     if (level_editor_test_game)
4551     {
4552       time = time_final;
4553       score = score_final;
4554
4555       local_player->LevelSolved_CountingTime = time;
4556       local_player->LevelSolved_CountingScore = score;
4557
4558       game_panel_controls[GAME_PANEL_TIME].value = time;
4559       game_panel_controls[GAME_PANEL_SCORE].value = score;
4560
4561       DisplayGameControlValues();
4562     }
4563
4564     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4565     {
4566       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4567       {
4568         /* close exit door after last player */
4569         if ((AllPlayersGone &&
4570              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4571               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4572               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4573             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4574             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4575         {
4576           int element = Feld[ExitX][ExitY];
4577
4578           Feld[ExitX][ExitY] =
4579             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4580              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4581              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4582              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4583              EL_EM_STEEL_EXIT_CLOSING);
4584
4585           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4586         }
4587
4588         /* player disappears */
4589         DrawLevelField(ExitX, ExitY);
4590       }
4591
4592       for (i = 0; i < MAX_PLAYERS; i++)
4593       {
4594         struct PlayerInfo *player = &stored_player[i];
4595
4596         if (player->present)
4597         {
4598           RemovePlayer(player);
4599
4600           /* player disappears */
4601           DrawLevelField(player->jx, player->jy);
4602         }
4603       }
4604     }
4605
4606     PlaySound(SND_GAME_WINNING);
4607   }
4608
4609   if (game_over_delay_1 > 0)
4610   {
4611     game_over_delay_1--;
4612
4613     return;
4614   }
4615
4616   if (time != time_final)
4617   {
4618     int time_to_go = ABS(time_final - time);
4619     int time_count_dir = (time < time_final ? +1 : -1);
4620
4621     if (time_to_go < time_count_steps)
4622       time_count_steps = 1;
4623
4624     time  += time_count_steps * time_count_dir;
4625     score += time_count_steps * level.score[SC_TIME_BONUS];
4626
4627     local_player->LevelSolved_CountingTime = time;
4628     local_player->LevelSolved_CountingScore = score;
4629
4630     game_panel_controls[GAME_PANEL_TIME].value = time;
4631     game_panel_controls[GAME_PANEL_SCORE].value = score;
4632
4633     DisplayGameControlValues();
4634
4635     if (time == time_final)
4636       StopSound(SND_GAME_LEVELTIME_BONUS);
4637     else if (setup.sound_loops)
4638       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4639     else
4640       PlaySound(SND_GAME_LEVELTIME_BONUS);
4641
4642     return;
4643   }
4644
4645   if (game_over_delay_2 > 0)
4646   {
4647     game_over_delay_2--;
4648
4649     return;
4650   }
4651
4652   if (health != health_final)
4653   {
4654     int health_count_dir = (health < health_final ? +1 : -1);
4655
4656     health += health_count_dir;
4657     score  += level.score[SC_TIME_BONUS];
4658
4659     local_player->LevelSolved_CountingHealth = health;
4660     local_player->LevelSolved_CountingScore = score;
4661
4662     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4663     game_panel_controls[GAME_PANEL_SCORE].value = score;
4664
4665     DisplayGameControlValues();
4666
4667     if (health == health_final)
4668       StopSound(SND_GAME_LEVELTIME_BONUS);
4669     else if (setup.sound_loops)
4670       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4671     else
4672       PlaySound(SND_GAME_LEVELTIME_BONUS);
4673
4674     return;
4675   }
4676
4677   local_player->LevelSolved_PanelOff = TRUE;
4678
4679   if (game_over_delay_3 > 0)
4680   {
4681     game_over_delay_3--;
4682
4683     return;
4684   }
4685
4686   GameEnd();
4687 }
4688
4689 void GameEnd()
4690 {
4691   int hi_pos;
4692   boolean raise_level = FALSE;
4693
4694   local_player->LevelSolved_GameEnd = TRUE;
4695
4696   if (local_player->LevelSolved_SaveTape)
4697   {
4698     /* make sure that request dialog to save tape does not open door again */
4699     if (!global.use_envelope_request)
4700       CloseDoor(DOOR_CLOSE_1);
4701
4702     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4703   }
4704
4705   /* if no tape is to be saved, close both doors simultaneously */
4706   CloseDoor(DOOR_CLOSE_ALL);
4707
4708   if (level_editor_test_game)
4709   {
4710     SetGameStatus(GAME_MODE_MAIN);
4711
4712     DrawMainMenu();
4713
4714     return;
4715   }
4716
4717   if (!local_player->LevelSolved_SaveScore)
4718   {
4719     SetGameStatus(GAME_MODE_MAIN);
4720
4721     DrawMainMenu();
4722
4723     return;
4724   }
4725
4726   if (level_nr == leveldir_current->handicap_level)
4727   {
4728     leveldir_current->handicap_level++;
4729
4730     SaveLevelSetup_SeriesInfo();
4731   }
4732
4733   if (setup.increment_levels &&
4734       level_nr < leveldir_current->last_level)
4735     raise_level = TRUE;                 /* advance to next level */
4736
4737   if ((hi_pos = NewHiScore()) >= 0) 
4738   {
4739     SetGameStatus(GAME_MODE_SCORES);
4740
4741     DrawHallOfFame(hi_pos);
4742
4743     if (raise_level)
4744     {
4745       level_nr++;
4746       TapeErase();
4747     }
4748   }
4749   else
4750   {
4751     SetGameStatus(GAME_MODE_MAIN);
4752
4753     if (raise_level)
4754     {
4755       level_nr++;
4756       TapeErase();
4757     }
4758
4759     DrawMainMenu();
4760   }
4761 }
4762
4763 int NewHiScore()
4764 {
4765   int k, l;
4766   int position = -1;
4767   boolean one_score_entry_per_name = !program.many_scores_per_name;
4768
4769   LoadScore(level_nr);
4770
4771   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4772       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4773     return -1;
4774
4775   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4776   {
4777     if (local_player->score_final > highscore[k].Score)
4778     {
4779       /* player has made it to the hall of fame */
4780
4781       if (k < MAX_SCORE_ENTRIES - 1)
4782       {
4783         int m = MAX_SCORE_ENTRIES - 1;
4784
4785         if (one_score_entry_per_name)
4786         {
4787           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4788             if (strEqual(setup.player_name, highscore[l].Name))
4789               m = l;
4790
4791           if (m == k)   /* player's new highscore overwrites his old one */
4792             goto put_into_list;
4793         }
4794
4795         for (l = m; l > k; l--)
4796         {
4797           strcpy(highscore[l].Name, highscore[l - 1].Name);
4798           highscore[l].Score = highscore[l - 1].Score;
4799         }
4800       }
4801
4802       put_into_list:
4803
4804       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4805       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4806       highscore[k].Score = local_player->score_final; 
4807       position = k;
4808
4809       break;
4810     }
4811     else if (one_score_entry_per_name &&
4812              !strncmp(setup.player_name, highscore[k].Name,
4813                       MAX_PLAYER_NAME_LEN))
4814       break;    /* player already there with a higher score */
4815   }
4816
4817   if (position >= 0) 
4818     SaveScore(level_nr);
4819
4820   return position;
4821 }
4822
4823 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4824 {
4825   int element = Feld[x][y];
4826   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4827   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4828   int horiz_move = (dx != 0);
4829   int sign = (horiz_move ? dx : dy);
4830   int step = sign * element_info[element].move_stepsize;
4831
4832   /* special values for move stepsize for spring and things on conveyor belt */
4833   if (horiz_move)
4834   {
4835     if (CAN_FALL(element) &&
4836         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4837       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4838     else if (element == EL_SPRING)
4839       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4840   }
4841
4842   return step;
4843 }
4844
4845 inline static int getElementMoveStepsize(int x, int y)
4846 {
4847   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4848 }
4849
4850 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4851 {
4852   if (player->GfxAction != action || player->GfxDir != dir)
4853   {
4854     player->GfxAction = action;
4855     player->GfxDir = dir;
4856     player->Frame = 0;
4857     player->StepFrame = 0;
4858   }
4859 }
4860
4861 static void ResetGfxFrame(int x, int y)
4862 {
4863   // profiling showed that "autotest" spends 10~20% of its time in this function
4864   if (DrawingDeactivatedField())
4865     return;
4866
4867   int element = Feld[x][y];
4868   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4869
4870   if (graphic_info[graphic].anim_global_sync)
4871     GfxFrame[x][y] = FrameCounter;
4872   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4873     GfxFrame[x][y] = CustomValue[x][y];
4874   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4875     GfxFrame[x][y] = element_info[element].collect_score;
4876   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4877     GfxFrame[x][y] = ChangeDelay[x][y];
4878 }
4879
4880 static void ResetGfxAnimation(int x, int y)
4881 {
4882   GfxAction[x][y] = ACTION_DEFAULT;
4883   GfxDir[x][y] = MovDir[x][y];
4884   GfxFrame[x][y] = 0;
4885
4886   ResetGfxFrame(x, y);
4887 }
4888
4889 static void ResetRandomAnimationValue(int x, int y)
4890 {
4891   GfxRandom[x][y] = INIT_GFX_RANDOM();
4892 }
4893
4894 void InitMovingField(int x, int y, int direction)
4895 {
4896   int element = Feld[x][y];
4897   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4898   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4899   int newx = x + dx;
4900   int newy = y + dy;
4901   boolean is_moving_before, is_moving_after;
4902
4903   /* check if element was/is moving or being moved before/after mode change */
4904   is_moving_before = (WasJustMoving[x][y] != 0);
4905   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4906
4907   /* reset animation only for moving elements which change direction of moving
4908      or which just started or stopped moving
4909      (else CEs with property "can move" / "not moving" are reset each frame) */
4910   if (is_moving_before != is_moving_after ||
4911       direction != MovDir[x][y])
4912     ResetGfxAnimation(x, y);
4913
4914   MovDir[x][y] = direction;
4915   GfxDir[x][y] = direction;
4916
4917   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4918                      direction == MV_DOWN && CAN_FALL(element) ?
4919                      ACTION_FALLING : ACTION_MOVING);
4920
4921   /* this is needed for CEs with property "can move" / "not moving" */
4922
4923   if (is_moving_after)
4924   {
4925     if (Feld[newx][newy] == EL_EMPTY)
4926       Feld[newx][newy] = EL_BLOCKED;
4927
4928     MovDir[newx][newy] = MovDir[x][y];
4929
4930     CustomValue[newx][newy] = CustomValue[x][y];
4931
4932     GfxFrame[newx][newy] = GfxFrame[x][y];
4933     GfxRandom[newx][newy] = GfxRandom[x][y];
4934     GfxAction[newx][newy] = GfxAction[x][y];
4935     GfxDir[newx][newy] = GfxDir[x][y];
4936   }
4937 }
4938
4939 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4940 {
4941   int direction = MovDir[x][y];
4942   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4943   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4944
4945   *goes_to_x = newx;
4946   *goes_to_y = newy;
4947 }
4948
4949 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4950 {
4951   int oldx = x, oldy = y;
4952   int direction = MovDir[x][y];
4953
4954   if (direction == MV_LEFT)
4955     oldx++;
4956   else if (direction == MV_RIGHT)
4957     oldx--;
4958   else if (direction == MV_UP)
4959     oldy++;
4960   else if (direction == MV_DOWN)
4961     oldy--;
4962
4963   *comes_from_x = oldx;
4964   *comes_from_y = oldy;
4965 }
4966
4967 int MovingOrBlocked2Element(int x, int y)
4968 {
4969   int element = Feld[x][y];
4970
4971   if (element == EL_BLOCKED)
4972   {
4973     int oldx, oldy;
4974
4975     Blocked2Moving(x, y, &oldx, &oldy);
4976     return Feld[oldx][oldy];
4977   }
4978   else
4979     return element;
4980 }
4981
4982 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4983 {
4984   /* like MovingOrBlocked2Element(), but if element is moving
4985      and (x,y) is the field the moving element is just leaving,
4986      return EL_BLOCKED instead of the element value */
4987   int element = Feld[x][y];
4988
4989   if (IS_MOVING(x, y))
4990   {
4991     if (element == EL_BLOCKED)
4992     {
4993       int oldx, oldy;
4994
4995       Blocked2Moving(x, y, &oldx, &oldy);
4996       return Feld[oldx][oldy];
4997     }
4998     else
4999       return EL_BLOCKED;
5000   }
5001   else
5002     return element;
5003 }
5004
5005 static void RemoveField(int x, int y)
5006 {
5007   Feld[x][y] = EL_EMPTY;
5008
5009   MovPos[x][y] = 0;
5010   MovDir[x][y] = 0;
5011   MovDelay[x][y] = 0;
5012
5013   CustomValue[x][y] = 0;
5014
5015   AmoebaNr[x][y] = 0;
5016   ChangeDelay[x][y] = 0;
5017   ChangePage[x][y] = -1;
5018   Pushed[x][y] = FALSE;
5019
5020   GfxElement[x][y] = EL_UNDEFINED;
5021   GfxAction[x][y] = ACTION_DEFAULT;
5022   GfxDir[x][y] = MV_NONE;
5023 }
5024
5025 void RemoveMovingField(int x, int y)
5026 {
5027   int oldx = x, oldy = y, newx = x, newy = y;
5028   int element = Feld[x][y];
5029   int next_element = EL_UNDEFINED;
5030
5031   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5032     return;
5033
5034   if (IS_MOVING(x, y))
5035   {
5036     Moving2Blocked(x, y, &newx, &newy);
5037
5038     if (Feld[newx][newy] != EL_BLOCKED)
5039     {
5040       /* element is moving, but target field is not free (blocked), but
5041          already occupied by something different (example: acid pool);
5042          in this case, only remove the moving field, but not the target */
5043
5044       RemoveField(oldx, oldy);
5045
5046       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5047
5048       TEST_DrawLevelField(oldx, oldy);
5049
5050       return;
5051     }
5052   }
5053   else if (element == EL_BLOCKED)
5054   {
5055     Blocked2Moving(x, y, &oldx, &oldy);
5056     if (!IS_MOVING(oldx, oldy))
5057       return;
5058   }
5059
5060   if (element == EL_BLOCKED &&
5061       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5062        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5063        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5064        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5065        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5066        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5067     next_element = get_next_element(Feld[oldx][oldy]);
5068
5069   RemoveField(oldx, oldy);
5070   RemoveField(newx, newy);
5071
5072   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5073
5074   if (next_element != EL_UNDEFINED)
5075     Feld[oldx][oldy] = next_element;
5076
5077   TEST_DrawLevelField(oldx, oldy);
5078   TEST_DrawLevelField(newx, newy);
5079 }
5080
5081 void DrawDynamite(int x, int y)
5082 {
5083   int sx = SCREENX(x), sy = SCREENY(y);
5084   int graphic = el2img(Feld[x][y]);
5085   int frame;
5086
5087   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5088     return;
5089
5090   if (IS_WALKABLE_INSIDE(Back[x][y]))
5091     return;
5092
5093   if (Back[x][y])
5094     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5095   else if (Store[x][y])
5096     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5097
5098   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5099
5100   if (Back[x][y] || Store[x][y])
5101     DrawGraphicThruMask(sx, sy, graphic, frame);
5102   else
5103     DrawGraphic(sx, sy, graphic, frame);
5104 }
5105
5106 void CheckDynamite(int x, int y)
5107 {
5108   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5109   {
5110     MovDelay[x][y]--;
5111
5112     if (MovDelay[x][y] != 0)
5113     {
5114       DrawDynamite(x, y);
5115       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5116
5117       return;
5118     }
5119   }
5120
5121   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5122
5123   Bang(x, y);
5124 }
5125
5126 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5127 {
5128   boolean num_checked_players = 0;
5129   int i;
5130
5131   for (i = 0; i < MAX_PLAYERS; i++)
5132   {
5133     if (stored_player[i].active)
5134     {
5135       int sx = stored_player[i].jx;
5136       int sy = stored_player[i].jy;
5137
5138       if (num_checked_players == 0)
5139       {
5140         *sx1 = *sx2 = sx;
5141         *sy1 = *sy2 = sy;
5142       }
5143       else
5144       {
5145         *sx1 = MIN(*sx1, sx);
5146         *sy1 = MIN(*sy1, sy);
5147         *sx2 = MAX(*sx2, sx);
5148         *sy2 = MAX(*sy2, sy);
5149       }
5150
5151       num_checked_players++;
5152     }
5153   }
5154 }
5155
5156 static boolean checkIfAllPlayersFitToScreen_RND()
5157 {
5158   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5159
5160   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5161
5162   return (sx2 - sx1 < SCR_FIELDX &&
5163           sy2 - sy1 < SCR_FIELDY);
5164 }
5165
5166 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5167 {
5168   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5169
5170   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5171
5172   *sx = (sx1 + sx2) / 2;
5173   *sy = (sy1 + sy2) / 2;
5174 }
5175
5176 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5177                         boolean center_screen, boolean quick_relocation)
5178 {
5179   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5180   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5181   boolean no_delay = (tape.warp_forward);
5182   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5183   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5184   int new_scroll_x, new_scroll_y;
5185
5186   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5187   {
5188     /* case 1: quick relocation inside visible screen (without scrolling) */
5189
5190     RedrawPlayfield();
5191
5192     return;
5193   }
5194
5195   if (!level.shifted_relocation || center_screen)
5196   {
5197     /* relocation _with_ centering of screen */
5198
5199     new_scroll_x = SCROLL_POSITION_X(x);
5200     new_scroll_y = SCROLL_POSITION_Y(y);
5201   }
5202   else
5203   {
5204     /* relocation _without_ centering of screen */
5205
5206     int center_scroll_x = SCROLL_POSITION_X(old_x);
5207     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5208     int offset_x = x + (scroll_x - center_scroll_x);
5209     int offset_y = y + (scroll_y - center_scroll_y);
5210
5211     /* for new screen position, apply previous offset to center position */
5212     new_scroll_x = SCROLL_POSITION_X(offset_x);
5213     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5214   }
5215
5216   if (quick_relocation)
5217   {
5218     /* case 2: quick relocation (redraw without visible scrolling) */
5219
5220     scroll_x = new_scroll_x;
5221     scroll_y = new_scroll_y;
5222
5223     RedrawPlayfield();
5224
5225     return;
5226   }
5227
5228   /* case 3: visible relocation (with scrolling to new position) */
5229
5230   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5231
5232   SetVideoFrameDelay(wait_delay_value);
5233
5234   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5235   {
5236     int dx = 0, dy = 0;
5237     int fx = FX, fy = FY;
5238
5239     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5240     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5241
5242     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5243       break;
5244
5245     scroll_x -= dx;
5246     scroll_y -= dy;
5247
5248     fx += dx * TILEX / 2;
5249     fy += dy * TILEY / 2;
5250
5251     ScrollLevel(dx, dy);
5252     DrawAllPlayers();
5253
5254     /* scroll in two steps of half tile size to make things smoother */
5255     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5256
5257     /* scroll second step to align at full tile size */
5258     BlitScreenToBitmap(window);
5259   }
5260
5261   DrawAllPlayers();
5262   BackToFront();
5263
5264   SetVideoFrameDelay(frame_delay_value_old);
5265 }
5266
5267 void RelocatePlayer(int jx, int jy, int el_player_raw)
5268 {
5269   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5270   int player_nr = GET_PLAYER_NR(el_player);
5271   struct PlayerInfo *player = &stored_player[player_nr];
5272   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5273   boolean no_delay = (tape.warp_forward);
5274   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5275   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5276   int old_jx = player->jx;
5277   int old_jy = player->jy;
5278   int old_element = Feld[old_jx][old_jy];
5279   int element = Feld[jx][jy];
5280   boolean player_relocated = (old_jx != jx || old_jy != jy);
5281
5282   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5283   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5284   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5285   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5286   int leave_side_horiz = move_dir_horiz;
5287   int leave_side_vert  = move_dir_vert;
5288   int enter_side = enter_side_horiz | enter_side_vert;
5289   int leave_side = leave_side_horiz | leave_side_vert;
5290
5291   if (player->GameOver)         /* do not reanimate dead player */
5292     return;
5293
5294   if (!player_relocated)        /* no need to relocate the player */
5295     return;
5296
5297   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5298   {
5299     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5300     DrawLevelField(jx, jy);
5301   }
5302
5303   if (player->present)
5304   {
5305     while (player->MovPos)
5306     {
5307       ScrollPlayer(player, SCROLL_GO_ON);
5308       ScrollScreen(NULL, SCROLL_GO_ON);
5309
5310       AdvanceFrameAndPlayerCounters(player->index_nr);
5311
5312       DrawPlayer(player);
5313
5314       BackToFront_WithFrameDelay(wait_delay_value);
5315     }
5316
5317     DrawPlayer(player);         /* needed here only to cleanup last field */
5318     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5319
5320     player->is_moving = FALSE;
5321   }
5322
5323   if (IS_CUSTOM_ELEMENT(old_element))
5324     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5325                                CE_LEFT_BY_PLAYER,
5326                                player->index_bit, leave_side);
5327
5328   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5329                                       CE_PLAYER_LEAVES_X,
5330                                       player->index_bit, leave_side);
5331
5332   Feld[jx][jy] = el_player;
5333   InitPlayerField(jx, jy, el_player, TRUE);
5334
5335   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5336      possible that the relocation target field did not contain a player element,
5337      but a walkable element, to which the new player was relocated -- in this
5338      case, restore that (already initialized!) element on the player field */
5339   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5340   {
5341     Feld[jx][jy] = element;     /* restore previously existing element */
5342   }
5343
5344   /* only visually relocate centered player */
5345   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5346                      FALSE, level.instant_relocation);
5347
5348   TestIfPlayerTouchesBadThing(jx, jy);
5349   TestIfPlayerTouchesCustomElement(jx, jy);
5350
5351   if (IS_CUSTOM_ELEMENT(element))
5352     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5353                                player->index_bit, enter_side);
5354
5355   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5356                                       player->index_bit, enter_side);
5357
5358   if (player->is_switching)
5359   {
5360     /* ensure that relocation while still switching an element does not cause
5361        a new element to be treated as also switched directly after relocation
5362        (this is important for teleporter switches that teleport the player to
5363        a place where another teleporter switch is in the same direction, which
5364        would then incorrectly be treated as immediately switched before the
5365        direction key that caused the switch was released) */
5366
5367     player->switch_x += jx - old_jx;
5368     player->switch_y += jy - old_jy;
5369   }
5370 }
5371
5372 void Explode(int ex, int ey, int phase, int mode)
5373 {
5374   int x, y;
5375   int last_phase;
5376   int border_element;
5377
5378   /* !!! eliminate this variable !!! */
5379   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5380
5381   if (game.explosions_delayed)
5382   {
5383     ExplodeField[ex][ey] = mode;
5384     return;
5385   }
5386
5387   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5388   {
5389     int center_element = Feld[ex][ey];
5390     int artwork_element, explosion_element;     /* set these values later */
5391
5392     /* remove things displayed in background while burning dynamite */
5393     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5394       Back[ex][ey] = 0;
5395
5396     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5397     {
5398       /* put moving element to center field (and let it explode there) */
5399       center_element = MovingOrBlocked2Element(ex, ey);
5400       RemoveMovingField(ex, ey);
5401       Feld[ex][ey] = center_element;
5402     }
5403
5404     /* now "center_element" is finally determined -- set related values now */
5405     artwork_element = center_element;           /* for custom player artwork */
5406     explosion_element = center_element;         /* for custom player artwork */
5407
5408     if (IS_PLAYER(ex, ey))
5409     {
5410       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5411
5412       artwork_element = stored_player[player_nr].artwork_element;
5413
5414       if (level.use_explosion_element[player_nr])
5415       {
5416         explosion_element = level.explosion_element[player_nr];
5417         artwork_element = explosion_element;
5418       }
5419     }
5420
5421     if (mode == EX_TYPE_NORMAL ||
5422         mode == EX_TYPE_CENTER ||
5423         mode == EX_TYPE_CROSS)
5424       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5425
5426     last_phase = element_info[explosion_element].explosion_delay + 1;
5427
5428     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5429     {
5430       int xx = x - ex + 1;
5431       int yy = y - ey + 1;
5432       int element;
5433
5434       if (!IN_LEV_FIELD(x, y) ||
5435           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5436           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5437         continue;
5438
5439       element = Feld[x][y];
5440
5441       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5442       {
5443         element = MovingOrBlocked2Element(x, y);
5444
5445         if (!IS_EXPLOSION_PROOF(element))
5446           RemoveMovingField(x, y);
5447       }
5448
5449       /* indestructible elements can only explode in center (but not flames) */
5450       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5451                                            mode == EX_TYPE_BORDER)) ||
5452           element == EL_FLAMES)
5453         continue;
5454
5455       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5456          behaviour, for example when touching a yamyam that explodes to rocks
5457          with active deadly shield, a rock is created under the player !!! */
5458       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5459 #if 0
5460       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5461           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5462            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5463 #else
5464       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5465 #endif
5466       {
5467         if (IS_ACTIVE_BOMB(element))
5468         {
5469           /* re-activate things under the bomb like gate or penguin */
5470           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5471           Back[x][y] = 0;
5472         }
5473
5474         continue;
5475       }
5476
5477       /* save walkable background elements while explosion on same tile */
5478       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5479           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5480         Back[x][y] = element;
5481
5482       /* ignite explodable elements reached by other explosion */
5483       if (element == EL_EXPLOSION)
5484         element = Store2[x][y];
5485
5486       if (AmoebaNr[x][y] &&
5487           (element == EL_AMOEBA_FULL ||
5488            element == EL_BD_AMOEBA ||
5489            element == EL_AMOEBA_GROWING))
5490       {
5491         AmoebaCnt[AmoebaNr[x][y]]--;
5492         AmoebaCnt2[AmoebaNr[x][y]]--;
5493       }
5494
5495       RemoveField(x, y);
5496
5497       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5498       {
5499         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5500
5501         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5502
5503         if (PLAYERINFO(ex, ey)->use_murphy)
5504           Store[x][y] = EL_EMPTY;
5505       }
5506
5507       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5508          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5509       else if (ELEM_IS_PLAYER(center_element))
5510         Store[x][y] = EL_EMPTY;
5511       else if (center_element == EL_YAMYAM)
5512         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5513       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5514         Store[x][y] = element_info[center_element].content.e[xx][yy];
5515 #if 1
5516       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5517          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5518          otherwise) -- FIX THIS !!! */
5519       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5520         Store[x][y] = element_info[element].content.e[1][1];
5521 #else
5522       else if (!CAN_EXPLODE(element))
5523         Store[x][y] = element_info[element].content.e[1][1];
5524 #endif
5525       else
5526         Store[x][y] = EL_EMPTY;
5527
5528       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5529           center_element == EL_AMOEBA_TO_DIAMOND)
5530         Store2[x][y] = element;
5531
5532       Feld[x][y] = EL_EXPLOSION;
5533       GfxElement[x][y] = artwork_element;
5534
5535       ExplodePhase[x][y] = 1;
5536       ExplodeDelay[x][y] = last_phase;
5537
5538       Stop[x][y] = TRUE;
5539     }
5540
5541     if (center_element == EL_YAMYAM)
5542       game.yamyam_content_nr =
5543         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5544
5545     return;
5546   }
5547
5548   if (Stop[ex][ey])
5549     return;
5550
5551   x = ex;
5552   y = ey;
5553
5554   if (phase == 1)
5555     GfxFrame[x][y] = 0;         /* restart explosion animation */
5556
5557   last_phase = ExplodeDelay[x][y];
5558
5559   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5560
5561   /* this can happen if the player leaves an explosion just in time */
5562   if (GfxElement[x][y] == EL_UNDEFINED)
5563     GfxElement[x][y] = EL_EMPTY;
5564
5565   border_element = Store2[x][y];
5566   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5567     border_element = StorePlayer[x][y];
5568
5569   if (phase == element_info[border_element].ignition_delay ||
5570       phase == last_phase)
5571   {
5572     boolean border_explosion = FALSE;
5573
5574     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5575         !PLAYER_EXPLOSION_PROTECTED(x, y))
5576     {
5577       KillPlayerUnlessExplosionProtected(x, y);
5578       border_explosion = TRUE;
5579     }
5580     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5581     {
5582       Feld[x][y] = Store2[x][y];
5583       Store2[x][y] = 0;
5584       Bang(x, y);
5585       border_explosion = TRUE;
5586     }
5587     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5588     {
5589       AmoebeUmwandeln(x, y);
5590       Store2[x][y] = 0;
5591       border_explosion = TRUE;
5592     }
5593
5594     /* if an element just explodes due to another explosion (chain-reaction),
5595        do not immediately end the new explosion when it was the last frame of
5596        the explosion (as it would be done in the following "if"-statement!) */
5597     if (border_explosion && phase == last_phase)
5598       return;
5599   }
5600
5601   if (phase == last_phase)
5602   {
5603     int element;
5604
5605     element = Feld[x][y] = Store[x][y];
5606     Store[x][y] = Store2[x][y] = 0;
5607     GfxElement[x][y] = EL_UNDEFINED;
5608
5609     /* player can escape from explosions and might therefore be still alive */
5610     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5611         element <= EL_PLAYER_IS_EXPLODING_4)
5612     {
5613       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5614       int explosion_element = EL_PLAYER_1 + player_nr;
5615       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5616       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5617
5618       if (level.use_explosion_element[player_nr])
5619         explosion_element = level.explosion_element[player_nr];
5620
5621       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5622                     element_info[explosion_element].content.e[xx][yy]);
5623     }
5624
5625     /* restore probably existing indestructible background element */
5626     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5627       element = Feld[x][y] = Back[x][y];
5628     Back[x][y] = 0;
5629
5630     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5631     GfxDir[x][y] = MV_NONE;
5632     ChangeDelay[x][y] = 0;
5633     ChangePage[x][y] = -1;
5634
5635     CustomValue[x][y] = 0;
5636
5637     InitField_WithBug2(x, y, FALSE);
5638
5639     TEST_DrawLevelField(x, y);
5640
5641     TestIfElementTouchesCustomElement(x, y);
5642
5643     if (GFX_CRUMBLED(element))
5644       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5645
5646     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5647       StorePlayer[x][y] = 0;
5648
5649     if (ELEM_IS_PLAYER(element))
5650       RelocatePlayer(x, y, element);
5651   }
5652   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5653   {
5654     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5655     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5656
5657     if (phase == delay)
5658       TEST_DrawLevelFieldCrumbled(x, y);
5659
5660     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5661     {
5662       DrawLevelElement(x, y, Back[x][y]);
5663       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5664     }
5665     else if (IS_WALKABLE_UNDER(Back[x][y]))
5666     {
5667       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5668       DrawLevelElementThruMask(x, y, Back[x][y]);
5669     }
5670     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5671       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5672   }
5673 }
5674
5675 void DynaExplode(int ex, int ey)
5676 {
5677   int i, j;
5678   int dynabomb_element = Feld[ex][ey];
5679   int dynabomb_size = 1;
5680   boolean dynabomb_xl = FALSE;
5681   struct PlayerInfo *player;
5682   static int xy[4][2] =
5683   {
5684     { 0, -1 },
5685     { -1, 0 },
5686     { +1, 0 },
5687     { 0, +1 }
5688   };
5689
5690   if (IS_ACTIVE_BOMB(dynabomb_element))
5691   {
5692     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5693     dynabomb_size = player->dynabomb_size;
5694     dynabomb_xl = player->dynabomb_xl;
5695     player->dynabombs_left++;
5696   }
5697
5698   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5699
5700   for (i = 0; i < NUM_DIRECTIONS; i++)
5701   {
5702     for (j = 1; j <= dynabomb_size; j++)
5703     {
5704       int x = ex + j * xy[i][0];
5705       int y = ey + j * xy[i][1];
5706       int element;
5707
5708       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5709         break;
5710
5711       element = Feld[x][y];
5712
5713       /* do not restart explosions of fields with active bombs */
5714       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5715         continue;
5716
5717       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5718
5719       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5720           !IS_DIGGABLE(element) && !dynabomb_xl)
5721         break;
5722     }
5723   }
5724 }
5725
5726 void Bang(int x, int y)
5727 {
5728   int element = MovingOrBlocked2Element(x, y);
5729   int explosion_type = EX_TYPE_NORMAL;
5730
5731   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5732   {
5733     struct PlayerInfo *player = PLAYERINFO(x, y);
5734
5735     element = Feld[x][y] = player->initial_element;
5736
5737     if (level.use_explosion_element[player->index_nr])
5738     {
5739       int explosion_element = level.explosion_element[player->index_nr];
5740
5741       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5742         explosion_type = EX_TYPE_CROSS;
5743       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5744         explosion_type = EX_TYPE_CENTER;
5745     }
5746   }
5747
5748   switch (element)
5749   {
5750     case EL_BUG:
5751     case EL_SPACESHIP:
5752     case EL_BD_BUTTERFLY:
5753     case EL_BD_FIREFLY:
5754     case EL_YAMYAM:
5755     case EL_DARK_YAMYAM:
5756     case EL_ROBOT:
5757     case EL_PACMAN:
5758     case EL_MOLE:
5759       RaiseScoreElement(element);
5760       break;
5761
5762     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5763     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5764     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5765     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5766     case EL_DYNABOMB_INCREASE_NUMBER:
5767     case EL_DYNABOMB_INCREASE_SIZE:
5768     case EL_DYNABOMB_INCREASE_POWER:
5769       explosion_type = EX_TYPE_DYNA;
5770       break;
5771
5772     case EL_DC_LANDMINE:
5773       explosion_type = EX_TYPE_CENTER;
5774       break;
5775
5776     case EL_PENGUIN:
5777     case EL_LAMP:
5778     case EL_LAMP_ACTIVE:
5779     case EL_AMOEBA_TO_DIAMOND:
5780       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5781         explosion_type = EX_TYPE_CENTER;
5782       break;
5783
5784     default:
5785       if (element_info[element].explosion_type == EXPLODES_CROSS)
5786         explosion_type = EX_TYPE_CROSS;
5787       else if (element_info[element].explosion_type == EXPLODES_1X1)
5788         explosion_type = EX_TYPE_CENTER;
5789       break;
5790   }
5791
5792   if (explosion_type == EX_TYPE_DYNA)
5793     DynaExplode(x, y);
5794   else
5795     Explode(x, y, EX_PHASE_START, explosion_type);
5796
5797   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5798 }
5799
5800 void SplashAcid(int x, int y)
5801 {
5802   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5803       (!IN_LEV_FIELD(x - 1, y - 2) ||
5804        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5805     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5806
5807   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5808       (!IN_LEV_FIELD(x + 1, y - 2) ||
5809        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5810     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5811
5812   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5813 }
5814
5815 static void InitBeltMovement()
5816 {
5817   static int belt_base_element[4] =
5818   {
5819     EL_CONVEYOR_BELT_1_LEFT,
5820     EL_CONVEYOR_BELT_2_LEFT,
5821     EL_CONVEYOR_BELT_3_LEFT,
5822     EL_CONVEYOR_BELT_4_LEFT
5823   };
5824   static int belt_base_active_element[4] =
5825   {
5826     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5827     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5828     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5829     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5830   };
5831
5832   int x, y, i, j;
5833
5834   /* set frame order for belt animation graphic according to belt direction */
5835   for (i = 0; i < NUM_BELTS; i++)
5836   {
5837     int belt_nr = i;
5838
5839     for (j = 0; j < NUM_BELT_PARTS; j++)
5840     {
5841       int element = belt_base_active_element[belt_nr] + j;
5842       int graphic_1 = el2img(element);
5843       int graphic_2 = el2panelimg(element);
5844
5845       if (game.belt_dir[i] == MV_LEFT)
5846       {
5847         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5848         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5849       }
5850       else
5851       {
5852         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5853         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5854       }
5855     }
5856   }
5857
5858   SCAN_PLAYFIELD(x, y)
5859   {
5860     int element = Feld[x][y];
5861
5862     for (i = 0; i < NUM_BELTS; i++)
5863     {
5864       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5865       {
5866         int e_belt_nr = getBeltNrFromBeltElement(element);
5867         int belt_nr = i;
5868
5869         if (e_belt_nr == belt_nr)
5870         {
5871           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5872
5873           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5874         }
5875       }
5876     }
5877   }
5878 }
5879
5880 static void ToggleBeltSwitch(int x, int y)
5881 {
5882   static int belt_base_element[4] =
5883   {
5884     EL_CONVEYOR_BELT_1_LEFT,
5885     EL_CONVEYOR_BELT_2_LEFT,
5886     EL_CONVEYOR_BELT_3_LEFT,
5887     EL_CONVEYOR_BELT_4_LEFT
5888   };
5889   static int belt_base_active_element[4] =
5890   {
5891     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5892     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5893     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5894     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5895   };
5896   static int belt_base_switch_element[4] =
5897   {
5898     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5899     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5900     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5901     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5902   };
5903   static int belt_move_dir[4] =
5904   {
5905     MV_LEFT,
5906     MV_NONE,
5907     MV_RIGHT,
5908     MV_NONE,
5909   };
5910
5911   int element = Feld[x][y];
5912   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5913   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5914   int belt_dir = belt_move_dir[belt_dir_nr];
5915   int xx, yy, i;
5916
5917   if (!IS_BELT_SWITCH(element))
5918     return;
5919
5920   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5921   game.belt_dir[belt_nr] = belt_dir;
5922
5923   if (belt_dir_nr == 3)
5924     belt_dir_nr = 1;
5925
5926   /* set frame order for belt animation graphic according to belt direction */
5927   for (i = 0; i < NUM_BELT_PARTS; i++)
5928   {
5929     int element = belt_base_active_element[belt_nr] + i;
5930     int graphic_1 = el2img(element);
5931     int graphic_2 = el2panelimg(element);
5932
5933     if (belt_dir == MV_LEFT)
5934     {
5935       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5936       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5937     }
5938     else
5939     {
5940       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5941       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5942     }
5943   }
5944
5945   SCAN_PLAYFIELD(xx, yy)
5946   {
5947     int element = Feld[xx][yy];
5948
5949     if (IS_BELT_SWITCH(element))
5950     {
5951       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5952
5953       if (e_belt_nr == belt_nr)
5954       {
5955         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5956         TEST_DrawLevelField(xx, yy);
5957       }
5958     }
5959     else if (IS_BELT(element) && belt_dir != MV_NONE)
5960     {
5961       int e_belt_nr = getBeltNrFromBeltElement(element);
5962
5963       if (e_belt_nr == belt_nr)
5964       {
5965         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5966
5967         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5968         TEST_DrawLevelField(xx, yy);
5969       }
5970     }
5971     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5972     {
5973       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5974
5975       if (e_belt_nr == belt_nr)
5976       {
5977         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5978
5979         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5980         TEST_DrawLevelField(xx, yy);
5981       }
5982     }
5983   }
5984 }
5985
5986 static void ToggleSwitchgateSwitch(int x, int y)
5987 {
5988   int xx, yy;
5989
5990   game.switchgate_pos = !game.switchgate_pos;
5991
5992   SCAN_PLAYFIELD(xx, yy)
5993   {
5994     int element = Feld[xx][yy];
5995
5996     if (element == EL_SWITCHGATE_SWITCH_UP)
5997     {
5998       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5999       TEST_DrawLevelField(xx, yy);
6000     }
6001     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6002     {
6003       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6004       TEST_DrawLevelField(xx, yy);
6005     }
6006     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6007     {
6008       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6009       TEST_DrawLevelField(xx, yy);
6010     }
6011     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6012     {
6013       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6014       TEST_DrawLevelField(xx, yy);
6015     }
6016     else if (element == EL_SWITCHGATE_OPEN ||
6017              element == EL_SWITCHGATE_OPENING)
6018     {
6019       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6020
6021       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6022     }
6023     else if (element == EL_SWITCHGATE_CLOSED ||
6024              element == EL_SWITCHGATE_CLOSING)
6025     {
6026       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6027
6028       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6029     }
6030   }
6031 }
6032
6033 static int getInvisibleActiveFromInvisibleElement(int element)
6034 {
6035   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6036           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6037           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6038           element);
6039 }
6040
6041 static int getInvisibleFromInvisibleActiveElement(int element)
6042 {
6043   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6044           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6045           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6046           element);
6047 }
6048
6049 static void RedrawAllLightSwitchesAndInvisibleElements()
6050 {
6051   int x, y;
6052
6053   SCAN_PLAYFIELD(x, y)
6054   {
6055     int element = Feld[x][y];
6056
6057     if (element == EL_LIGHT_SWITCH &&
6058         game.light_time_left > 0)
6059     {
6060       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6061       TEST_DrawLevelField(x, y);
6062     }
6063     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6064              game.light_time_left == 0)
6065     {
6066       Feld[x][y] = EL_LIGHT_SWITCH;
6067       TEST_DrawLevelField(x, y);
6068     }
6069     else if (element == EL_EMC_DRIPPER &&
6070              game.light_time_left > 0)
6071     {
6072       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6073       TEST_DrawLevelField(x, y);
6074     }
6075     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6076              game.light_time_left == 0)
6077     {
6078       Feld[x][y] = EL_EMC_DRIPPER;
6079       TEST_DrawLevelField(x, y);
6080     }
6081     else if (element == EL_INVISIBLE_STEELWALL ||
6082              element == EL_INVISIBLE_WALL ||
6083              element == EL_INVISIBLE_SAND)
6084     {
6085       if (game.light_time_left > 0)
6086         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6087
6088       TEST_DrawLevelField(x, y);
6089
6090       /* uncrumble neighbour fields, if needed */
6091       if (element == EL_INVISIBLE_SAND)
6092         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6093     }
6094     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6095              element == EL_INVISIBLE_WALL_ACTIVE ||
6096              element == EL_INVISIBLE_SAND_ACTIVE)
6097     {
6098       if (game.light_time_left == 0)
6099         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6100
6101       TEST_DrawLevelField(x, y);
6102
6103       /* re-crumble neighbour fields, if needed */
6104       if (element == EL_INVISIBLE_SAND)
6105         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6106     }
6107   }
6108 }
6109
6110 static void RedrawAllInvisibleElementsForLenses()
6111 {
6112   int x, y;
6113
6114   SCAN_PLAYFIELD(x, y)
6115   {
6116     int element = Feld[x][y];
6117
6118     if (element == EL_EMC_DRIPPER &&
6119         game.lenses_time_left > 0)
6120     {
6121       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6122       TEST_DrawLevelField(x, y);
6123     }
6124     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6125              game.lenses_time_left == 0)
6126     {
6127       Feld[x][y] = EL_EMC_DRIPPER;
6128       TEST_DrawLevelField(x, y);
6129     }
6130     else if (element == EL_INVISIBLE_STEELWALL ||
6131              element == EL_INVISIBLE_WALL ||
6132              element == EL_INVISIBLE_SAND)
6133     {
6134       if (game.lenses_time_left > 0)
6135         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6136
6137       TEST_DrawLevelField(x, y);
6138
6139       /* uncrumble neighbour fields, if needed */
6140       if (element == EL_INVISIBLE_SAND)
6141         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6142     }
6143     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6144              element == EL_INVISIBLE_WALL_ACTIVE ||
6145              element == EL_INVISIBLE_SAND_ACTIVE)
6146     {
6147       if (game.lenses_time_left == 0)
6148         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6149
6150       TEST_DrawLevelField(x, y);
6151
6152       /* re-crumble neighbour fields, if needed */
6153       if (element == EL_INVISIBLE_SAND)
6154         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6155     }
6156   }
6157 }
6158
6159 static void RedrawAllInvisibleElementsForMagnifier()
6160 {
6161   int x, y;
6162
6163   SCAN_PLAYFIELD(x, y)
6164   {
6165     int element = Feld[x][y];
6166
6167     if (element == EL_EMC_FAKE_GRASS &&
6168         game.magnify_time_left > 0)
6169     {
6170       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6171       TEST_DrawLevelField(x, y);
6172     }
6173     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6174              game.magnify_time_left == 0)
6175     {
6176       Feld[x][y] = EL_EMC_FAKE_GRASS;
6177       TEST_DrawLevelField(x, y);
6178     }
6179     else if (IS_GATE_GRAY(element) &&
6180              game.magnify_time_left > 0)
6181     {
6182       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6183                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6184                     IS_EM_GATE_GRAY(element) ?
6185                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6186                     IS_EMC_GATE_GRAY(element) ?
6187                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6188                     IS_DC_GATE_GRAY(element) ?
6189                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6190                     element);
6191       TEST_DrawLevelField(x, y);
6192     }
6193     else if (IS_GATE_GRAY_ACTIVE(element) &&
6194              game.magnify_time_left == 0)
6195     {
6196       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6197                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6198                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6199                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6200                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6201                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6202                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6203                     EL_DC_GATE_WHITE_GRAY :
6204                     element);
6205       TEST_DrawLevelField(x, y);
6206     }
6207   }
6208 }
6209
6210 static void ToggleLightSwitch(int x, int y)
6211 {
6212   int element = Feld[x][y];
6213
6214   game.light_time_left =
6215     (element == EL_LIGHT_SWITCH ?
6216      level.time_light * FRAMES_PER_SECOND : 0);
6217
6218   RedrawAllLightSwitchesAndInvisibleElements();
6219 }
6220
6221 static void ActivateTimegateSwitch(int x, int y)
6222 {
6223   int xx, yy;
6224
6225   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6226
6227   SCAN_PLAYFIELD(xx, yy)
6228   {
6229     int element = Feld[xx][yy];
6230
6231     if (element == EL_TIMEGATE_CLOSED ||
6232         element == EL_TIMEGATE_CLOSING)
6233     {
6234       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6235       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6236     }
6237
6238     /*
6239     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6240     {
6241       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6242       TEST_DrawLevelField(xx, yy);
6243     }
6244     */
6245
6246   }
6247
6248   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6249                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6250 }
6251
6252 void Impact(int x, int y)
6253 {
6254   boolean last_line = (y == lev_fieldy - 1);
6255   boolean object_hit = FALSE;
6256   boolean impact = (last_line || object_hit);
6257   int element = Feld[x][y];
6258   int smashed = EL_STEELWALL;
6259
6260   if (!last_line)       /* check if element below was hit */
6261   {
6262     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6263       return;
6264
6265     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6266                                          MovDir[x][y + 1] != MV_DOWN ||
6267                                          MovPos[x][y + 1] <= TILEY / 2));
6268
6269     /* do not smash moving elements that left the smashed field in time */
6270     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6271         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6272       object_hit = FALSE;
6273
6274 #if USE_QUICKSAND_IMPACT_BUGFIX
6275     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6276     {
6277       RemoveMovingField(x, y + 1);
6278       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6279       Feld[x][y + 2] = EL_ROCK;
6280       TEST_DrawLevelField(x, y + 2);
6281
6282       object_hit = TRUE;
6283     }
6284
6285     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6286     {
6287       RemoveMovingField(x, y + 1);
6288       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6289       Feld[x][y + 2] = EL_ROCK;
6290       TEST_DrawLevelField(x, y + 2);
6291
6292       object_hit = TRUE;
6293     }
6294 #endif
6295
6296     if (object_hit)
6297       smashed = MovingOrBlocked2Element(x, y + 1);
6298
6299     impact = (last_line || object_hit);
6300   }
6301
6302   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6303   {
6304     SplashAcid(x, y + 1);
6305     return;
6306   }
6307
6308   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6309   /* only reset graphic animation if graphic really changes after impact */
6310   if (impact &&
6311       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6312   {
6313     ResetGfxAnimation(x, y);
6314     TEST_DrawLevelField(x, y);
6315   }
6316
6317   if (impact && CAN_EXPLODE_IMPACT(element))
6318   {
6319     Bang(x, y);
6320     return;
6321   }
6322   else if (impact && element == EL_PEARL &&
6323            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6324   {
6325     ResetGfxAnimation(x, y);
6326
6327     Feld[x][y] = EL_PEARL_BREAKING;
6328     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6329     return;
6330   }
6331   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6332   {
6333     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6334
6335     return;
6336   }
6337
6338   if (impact && element == EL_AMOEBA_DROP)
6339   {
6340     if (object_hit && IS_PLAYER(x, y + 1))
6341       KillPlayerUnlessEnemyProtected(x, y + 1);
6342     else if (object_hit && smashed == EL_PENGUIN)
6343       Bang(x, y + 1);
6344     else
6345     {
6346       Feld[x][y] = EL_AMOEBA_GROWING;
6347       Store[x][y] = EL_AMOEBA_WET;
6348
6349       ResetRandomAnimationValue(x, y);
6350     }
6351     return;
6352   }
6353
6354   if (object_hit)               /* check which object was hit */
6355   {
6356     if ((CAN_PASS_MAGIC_WALL(element) && 
6357          (smashed == EL_MAGIC_WALL ||
6358           smashed == EL_BD_MAGIC_WALL)) ||
6359         (CAN_PASS_DC_MAGIC_WALL(element) &&
6360          smashed == EL_DC_MAGIC_WALL))
6361     {
6362       int xx, yy;
6363       int activated_magic_wall =
6364         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6365          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6366          EL_DC_MAGIC_WALL_ACTIVE);
6367
6368       /* activate magic wall / mill */
6369       SCAN_PLAYFIELD(xx, yy)
6370       {
6371         if (Feld[xx][yy] == smashed)
6372           Feld[xx][yy] = activated_magic_wall;
6373       }
6374
6375       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6376       game.magic_wall_active = TRUE;
6377
6378       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6379                             SND_MAGIC_WALL_ACTIVATING :
6380                             smashed == EL_BD_MAGIC_WALL ?
6381                             SND_BD_MAGIC_WALL_ACTIVATING :
6382                             SND_DC_MAGIC_WALL_ACTIVATING));
6383     }
6384
6385     if (IS_PLAYER(x, y + 1))
6386     {
6387       if (CAN_SMASH_PLAYER(element))
6388       {
6389         KillPlayerUnlessEnemyProtected(x, y + 1);
6390         return;
6391       }
6392     }
6393     else if (smashed == EL_PENGUIN)
6394     {
6395       if (CAN_SMASH_PLAYER(element))
6396       {
6397         Bang(x, y + 1);
6398         return;
6399       }
6400     }
6401     else if (element == EL_BD_DIAMOND)
6402     {
6403       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6404       {
6405         Bang(x, y + 1);
6406         return;
6407       }
6408     }
6409     else if (((element == EL_SP_INFOTRON ||
6410                element == EL_SP_ZONK) &&
6411               (smashed == EL_SP_SNIKSNAK ||
6412                smashed == EL_SP_ELECTRON ||
6413                smashed == EL_SP_DISK_ORANGE)) ||
6414              (element == EL_SP_INFOTRON &&
6415               smashed == EL_SP_DISK_YELLOW))
6416     {
6417       Bang(x, y + 1);
6418       return;
6419     }
6420     else if (CAN_SMASH_EVERYTHING(element))
6421     {
6422       if (IS_CLASSIC_ENEMY(smashed) ||
6423           CAN_EXPLODE_SMASHED(smashed))
6424       {
6425         Bang(x, y + 1);
6426         return;
6427       }
6428       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6429       {
6430         if (smashed == EL_LAMP ||
6431             smashed == EL_LAMP_ACTIVE)
6432         {
6433           Bang(x, y + 1);
6434           return;
6435         }
6436         else if (smashed == EL_NUT)
6437         {
6438           Feld[x][y + 1] = EL_NUT_BREAKING;
6439           PlayLevelSound(x, y, SND_NUT_BREAKING);
6440           RaiseScoreElement(EL_NUT);
6441           return;
6442         }
6443         else if (smashed == EL_PEARL)
6444         {
6445           ResetGfxAnimation(x, y);
6446
6447           Feld[x][y + 1] = EL_PEARL_BREAKING;
6448           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6449           return;
6450         }
6451         else if (smashed == EL_DIAMOND)
6452         {
6453           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6454           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6455           return;
6456         }
6457         else if (IS_BELT_SWITCH(smashed))
6458         {
6459           ToggleBeltSwitch(x, y + 1);
6460         }
6461         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6462                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6463                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6464                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6465         {
6466           ToggleSwitchgateSwitch(x, y + 1);
6467         }
6468         else if (smashed == EL_LIGHT_SWITCH ||
6469                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6470         {
6471           ToggleLightSwitch(x, y + 1);
6472         }
6473         else
6474         {
6475           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6476
6477           CheckElementChangeBySide(x, y + 1, smashed, element,
6478                                    CE_SWITCHED, CH_SIDE_TOP);
6479           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6480                                             CH_SIDE_TOP);
6481         }
6482       }
6483       else
6484       {
6485         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6486       }
6487     }
6488   }
6489
6490   /* play sound of magic wall / mill */
6491   if (!last_line &&
6492       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6493        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6494        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6495   {
6496     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6497       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6498     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6499       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6500     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6501       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6502
6503     return;
6504   }
6505
6506   /* play sound of object that hits the ground */
6507   if (last_line || object_hit)
6508     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6509 }
6510
6511 inline static void TurnRoundExt(int x, int y)
6512 {
6513   static struct
6514   {
6515     int dx, dy;
6516   } move_xy[] =
6517   {
6518     {  0,  0 },
6519     { -1,  0 },
6520     { +1,  0 },
6521     {  0,  0 },
6522     {  0, -1 },
6523     {  0,  0 }, { 0, 0 }, { 0, 0 },
6524     {  0, +1 }
6525   };
6526   static struct
6527   {
6528     int left, right, back;
6529   } turn[] =
6530   {
6531     { 0,        0,              0        },
6532     { MV_DOWN,  MV_UP,          MV_RIGHT },
6533     { MV_UP,    MV_DOWN,        MV_LEFT  },
6534     { 0,        0,              0        },
6535     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6536     { 0,        0,              0        },
6537     { 0,        0,              0        },
6538     { 0,        0,              0        },
6539     { MV_RIGHT, MV_LEFT,        MV_UP    }
6540   };
6541
6542   int element = Feld[x][y];
6543   int move_pattern = element_info[element].move_pattern;
6544
6545   int old_move_dir = MovDir[x][y];
6546   int left_dir  = turn[old_move_dir].left;
6547   int right_dir = turn[old_move_dir].right;
6548   int back_dir  = turn[old_move_dir].back;
6549
6550   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6551   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6552   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6553   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6554
6555   int left_x  = x + left_dx,  left_y  = y + left_dy;
6556   int right_x = x + right_dx, right_y = y + right_dy;
6557   int move_x  = x + move_dx,  move_y  = y + move_dy;
6558
6559   int xx, yy;
6560
6561   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6562   {
6563     TestIfBadThingTouchesOtherBadThing(x, y);
6564
6565     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6566       MovDir[x][y] = right_dir;
6567     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6568       MovDir[x][y] = left_dir;
6569
6570     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6571       MovDelay[x][y] = 9;
6572     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6573       MovDelay[x][y] = 1;
6574   }
6575   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6576   {
6577     TestIfBadThingTouchesOtherBadThing(x, y);
6578
6579     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6580       MovDir[x][y] = left_dir;
6581     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6582       MovDir[x][y] = right_dir;
6583
6584     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6585       MovDelay[x][y] = 9;
6586     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6587       MovDelay[x][y] = 1;
6588   }
6589   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6590   {
6591     TestIfBadThingTouchesOtherBadThing(x, y);
6592
6593     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6594       MovDir[x][y] = left_dir;
6595     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6596       MovDir[x][y] = right_dir;
6597
6598     if (MovDir[x][y] != old_move_dir)
6599       MovDelay[x][y] = 9;
6600   }
6601   else if (element == EL_YAMYAM)
6602   {
6603     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6604     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6605
6606     if (can_turn_left && can_turn_right)
6607       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608     else if (can_turn_left)
6609       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610     else if (can_turn_right)
6611       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6612     else
6613       MovDir[x][y] = back_dir;
6614
6615     MovDelay[x][y] = 16 + 16 * RND(3);
6616   }
6617   else if (element == EL_DARK_YAMYAM)
6618   {
6619     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6620                                                          left_x, left_y);
6621     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6622                                                          right_x, right_y);
6623
6624     if (can_turn_left && can_turn_right)
6625       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6626     else if (can_turn_left)
6627       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6628     else if (can_turn_right)
6629       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6630     else
6631       MovDir[x][y] = back_dir;
6632
6633     MovDelay[x][y] = 16 + 16 * RND(3);
6634   }
6635   else if (element == EL_PACMAN)
6636   {
6637     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6638     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6639
6640     if (can_turn_left && can_turn_right)
6641       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6642     else if (can_turn_left)
6643       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6644     else if (can_turn_right)
6645       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6646     else
6647       MovDir[x][y] = back_dir;
6648
6649     MovDelay[x][y] = 6 + RND(40);
6650   }
6651   else if (element == EL_PIG)
6652   {
6653     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6654     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6655     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6656     boolean should_turn_left, should_turn_right, should_move_on;
6657     int rnd_value = 24;
6658     int rnd = RND(rnd_value);
6659
6660     should_turn_left = (can_turn_left &&
6661                         (!can_move_on ||
6662                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6663                                                    y + back_dy + left_dy)));
6664     should_turn_right = (can_turn_right &&
6665                          (!can_move_on ||
6666                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6667                                                     y + back_dy + right_dy)));
6668     should_move_on = (can_move_on &&
6669                       (!can_turn_left ||
6670                        !can_turn_right ||
6671                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6672                                                  y + move_dy + left_dy) ||
6673                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6674                                                  y + move_dy + right_dy)));
6675
6676     if (should_turn_left || should_turn_right || should_move_on)
6677     {
6678       if (should_turn_left && should_turn_right && should_move_on)
6679         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6680                         rnd < 2 * rnd_value / 3 ? right_dir :
6681                         old_move_dir);
6682       else if (should_turn_left && should_turn_right)
6683         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6684       else if (should_turn_left && should_move_on)
6685         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6686       else if (should_turn_right && should_move_on)
6687         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6688       else if (should_turn_left)
6689         MovDir[x][y] = left_dir;
6690       else if (should_turn_right)
6691         MovDir[x][y] = right_dir;
6692       else if (should_move_on)
6693         MovDir[x][y] = old_move_dir;
6694     }
6695     else if (can_move_on && rnd > rnd_value / 8)
6696       MovDir[x][y] = old_move_dir;
6697     else if (can_turn_left && can_turn_right)
6698       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6699     else if (can_turn_left && rnd > rnd_value / 8)
6700       MovDir[x][y] = left_dir;
6701     else if (can_turn_right && rnd > rnd_value/8)
6702       MovDir[x][y] = right_dir;
6703     else
6704       MovDir[x][y] = back_dir;
6705
6706     xx = x + move_xy[MovDir[x][y]].dx;
6707     yy = y + move_xy[MovDir[x][y]].dy;
6708
6709     if (!IN_LEV_FIELD(xx, yy) ||
6710         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6711       MovDir[x][y] = old_move_dir;
6712
6713     MovDelay[x][y] = 0;
6714   }
6715   else if (element == EL_DRAGON)
6716   {
6717     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6718     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6719     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6720     int rnd_value = 24;
6721     int rnd = RND(rnd_value);
6722
6723     if (can_move_on && rnd > rnd_value / 8)
6724       MovDir[x][y] = old_move_dir;
6725     else if (can_turn_left && can_turn_right)
6726       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6727     else if (can_turn_left && rnd > rnd_value / 8)
6728       MovDir[x][y] = left_dir;
6729     else if (can_turn_right && rnd > rnd_value / 8)
6730       MovDir[x][y] = right_dir;
6731     else
6732       MovDir[x][y] = back_dir;
6733
6734     xx = x + move_xy[MovDir[x][y]].dx;
6735     yy = y + move_xy[MovDir[x][y]].dy;
6736
6737     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6738       MovDir[x][y] = old_move_dir;
6739
6740     MovDelay[x][y] = 0;
6741   }
6742   else if (element == EL_MOLE)
6743   {
6744     boolean can_move_on =
6745       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6746                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6747                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6748     if (!can_move_on)
6749     {
6750       boolean can_turn_left =
6751         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6752                               IS_AMOEBOID(Feld[left_x][left_y])));
6753
6754       boolean can_turn_right =
6755         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6756                               IS_AMOEBOID(Feld[right_x][right_y])));
6757
6758       if (can_turn_left && can_turn_right)
6759         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6760       else if (can_turn_left)
6761         MovDir[x][y] = left_dir;
6762       else
6763         MovDir[x][y] = right_dir;
6764     }
6765
6766     if (MovDir[x][y] != old_move_dir)
6767       MovDelay[x][y] = 9;
6768   }
6769   else if (element == EL_BALLOON)
6770   {
6771     MovDir[x][y] = game.wind_direction;
6772     MovDelay[x][y] = 0;
6773   }
6774   else if (element == EL_SPRING)
6775   {
6776     if (MovDir[x][y] & MV_HORIZONTAL)
6777     {
6778       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6779           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6780       {
6781         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6782         ResetGfxAnimation(move_x, move_y);
6783         TEST_DrawLevelField(move_x, move_y);
6784
6785         MovDir[x][y] = back_dir;
6786       }
6787       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6788                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6789         MovDir[x][y] = MV_NONE;
6790     }
6791
6792     MovDelay[x][y] = 0;
6793   }
6794   else if (element == EL_ROBOT ||
6795            element == EL_SATELLITE ||
6796            element == EL_PENGUIN ||
6797            element == EL_EMC_ANDROID)
6798   {
6799     int attr_x = -1, attr_y = -1;
6800
6801     if (AllPlayersGone)
6802     {
6803       attr_x = ExitX;
6804       attr_y = ExitY;
6805     }
6806     else
6807     {
6808       int i;
6809
6810       for (i = 0; i < MAX_PLAYERS; i++)
6811       {
6812         struct PlayerInfo *player = &stored_player[i];
6813         int jx = player->jx, jy = player->jy;
6814
6815         if (!player->active)
6816           continue;
6817
6818         if (attr_x == -1 ||
6819             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6820         {
6821           attr_x = jx;
6822           attr_y = jy;
6823         }
6824       }
6825     }
6826
6827     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6828         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6829          game.engine_version < VERSION_IDENT(3,1,0,0)))
6830     {
6831       attr_x = ZX;
6832       attr_y = ZY;
6833     }
6834
6835     if (element == EL_PENGUIN)
6836     {
6837       int i;
6838       static int xy[4][2] =
6839       {
6840         { 0, -1 },
6841         { -1, 0 },
6842         { +1, 0 },
6843         { 0, +1 }
6844       };
6845
6846       for (i = 0; i < NUM_DIRECTIONS; i++)
6847       {
6848         int ex = x + xy[i][0];
6849         int ey = y + xy[i][1];
6850
6851         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6852                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6853                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6854                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6855         {
6856           attr_x = ex;
6857           attr_y = ey;
6858           break;
6859         }
6860       }
6861     }
6862
6863     MovDir[x][y] = MV_NONE;
6864     if (attr_x < x)
6865       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6866     else if (attr_x > x)
6867       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6868     if (attr_y < y)
6869       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6870     else if (attr_y > y)
6871       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6872
6873     if (element == EL_ROBOT)
6874     {
6875       int newx, newy;
6876
6877       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6878         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6879       Moving2Blocked(x, y, &newx, &newy);
6880
6881       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6882         MovDelay[x][y] = 8 + 8 * !RND(3);
6883       else
6884         MovDelay[x][y] = 16;
6885     }
6886     else if (element == EL_PENGUIN)
6887     {
6888       int newx, newy;
6889
6890       MovDelay[x][y] = 1;
6891
6892       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6893       {
6894         boolean first_horiz = RND(2);
6895         int new_move_dir = MovDir[x][y];
6896
6897         MovDir[x][y] =
6898           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6899         Moving2Blocked(x, y, &newx, &newy);
6900
6901         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6902           return;
6903
6904         MovDir[x][y] =
6905           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6906         Moving2Blocked(x, y, &newx, &newy);
6907
6908         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6909           return;
6910
6911         MovDir[x][y] = old_move_dir;
6912         return;
6913       }
6914     }
6915     else if (element == EL_SATELLITE)
6916     {
6917       int newx, newy;
6918
6919       MovDelay[x][y] = 1;
6920
6921       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6922       {
6923         boolean first_horiz = RND(2);
6924         int new_move_dir = MovDir[x][y];
6925
6926         MovDir[x][y] =
6927           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6928         Moving2Blocked(x, y, &newx, &newy);
6929
6930         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6931           return;
6932
6933         MovDir[x][y] =
6934           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6935         Moving2Blocked(x, y, &newx, &newy);
6936
6937         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6938           return;
6939
6940         MovDir[x][y] = old_move_dir;
6941         return;
6942       }
6943     }
6944     else if (element == EL_EMC_ANDROID)
6945     {
6946       static int check_pos[16] =
6947       {
6948         -1,             /*  0 => (invalid)          */
6949         7,              /*  1 => MV_LEFT            */
6950         3,              /*  2 => MV_RIGHT           */
6951         -1,             /*  3 => (invalid)          */
6952         1,              /*  4 =>            MV_UP   */
6953         0,              /*  5 => MV_LEFT  | MV_UP   */
6954         2,              /*  6 => MV_RIGHT | MV_UP   */
6955         -1,             /*  7 => (invalid)          */
6956         5,              /*  8 =>            MV_DOWN */
6957         6,              /*  9 => MV_LEFT  | MV_DOWN */
6958         4,              /* 10 => MV_RIGHT | MV_DOWN */
6959         -1,             /* 11 => (invalid)          */
6960         -1,             /* 12 => (invalid)          */
6961         -1,             /* 13 => (invalid)          */
6962         -1,             /* 14 => (invalid)          */
6963         -1,             /* 15 => (invalid)          */
6964       };
6965       static struct
6966       {
6967         int dx, dy;
6968         int dir;
6969       } check_xy[8] =
6970       {
6971         { -1, -1,       MV_LEFT  | MV_UP   },
6972         {  0, -1,                  MV_UP   },
6973         { +1, -1,       MV_RIGHT | MV_UP   },
6974         { +1,  0,       MV_RIGHT           },
6975         { +1, +1,       MV_RIGHT | MV_DOWN },
6976         {  0, +1,                  MV_DOWN },
6977         { -1, +1,       MV_LEFT  | MV_DOWN },
6978         { -1,  0,       MV_LEFT            },
6979       };
6980       int start_pos, check_order;
6981       boolean can_clone = FALSE;
6982       int i;
6983
6984       /* check if there is any free field around current position */
6985       for (i = 0; i < 8; i++)
6986       {
6987         int newx = x + check_xy[i].dx;
6988         int newy = y + check_xy[i].dy;
6989
6990         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6991         {
6992           can_clone = TRUE;
6993
6994           break;
6995         }
6996       }
6997
6998       if (can_clone)            /* randomly find an element to clone */
6999       {
7000         can_clone = FALSE;
7001
7002         start_pos = check_pos[RND(8)];
7003         check_order = (RND(2) ? -1 : +1);
7004
7005         for (i = 0; i < 8; i++)
7006         {
7007           int pos_raw = start_pos + i * check_order;
7008           int pos = (pos_raw + 8) % 8;
7009           int newx = x + check_xy[pos].dx;
7010           int newy = y + check_xy[pos].dy;
7011
7012           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7013           {
7014             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7015             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7016
7017             Store[x][y] = Feld[newx][newy];
7018
7019             can_clone = TRUE;
7020
7021             break;
7022           }
7023         }
7024       }
7025
7026       if (can_clone)            /* randomly find a direction to move */
7027       {
7028         can_clone = FALSE;
7029
7030         start_pos = check_pos[RND(8)];
7031         check_order = (RND(2) ? -1 : +1);
7032
7033         for (i = 0; i < 8; i++)
7034         {
7035           int pos_raw = start_pos + i * check_order;
7036           int pos = (pos_raw + 8) % 8;
7037           int newx = x + check_xy[pos].dx;
7038           int newy = y + check_xy[pos].dy;
7039           int new_move_dir = check_xy[pos].dir;
7040
7041           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7042           {
7043             MovDir[x][y] = new_move_dir;
7044             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7045
7046             can_clone = TRUE;
7047
7048             break;
7049           }
7050         }
7051       }
7052
7053       if (can_clone)            /* cloning and moving successful */
7054         return;
7055
7056       /* cannot clone -- try to move towards player */
7057
7058       start_pos = check_pos[MovDir[x][y] & 0x0f];
7059       check_order = (RND(2) ? -1 : +1);
7060
7061       for (i = 0; i < 3; i++)
7062       {
7063         /* first check start_pos, then previous/next or (next/previous) pos */
7064         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7065         int pos = (pos_raw + 8) % 8;
7066         int newx = x + check_xy[pos].dx;
7067         int newy = y + check_xy[pos].dy;
7068         int new_move_dir = check_xy[pos].dir;
7069
7070         if (IS_PLAYER(newx, newy))
7071           break;
7072
7073         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7074         {
7075           MovDir[x][y] = new_move_dir;
7076           MovDelay[x][y] = level.android_move_time * 8 + 1;
7077
7078           break;
7079         }
7080       }
7081     }
7082   }
7083   else if (move_pattern == MV_TURNING_LEFT ||
7084            move_pattern == MV_TURNING_RIGHT ||
7085            move_pattern == MV_TURNING_LEFT_RIGHT ||
7086            move_pattern == MV_TURNING_RIGHT_LEFT ||
7087            move_pattern == MV_TURNING_RANDOM ||
7088            move_pattern == MV_ALL_DIRECTIONS)
7089   {
7090     boolean can_turn_left =
7091       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7092     boolean can_turn_right =
7093       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7094
7095     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7096       return;
7097
7098     if (move_pattern == MV_TURNING_LEFT)
7099       MovDir[x][y] = left_dir;
7100     else if (move_pattern == MV_TURNING_RIGHT)
7101       MovDir[x][y] = right_dir;
7102     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7103       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7104     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7105       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7106     else if (move_pattern == MV_TURNING_RANDOM)
7107       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7108                       can_turn_right && !can_turn_left ? right_dir :
7109                       RND(2) ? left_dir : right_dir);
7110     else if (can_turn_left && can_turn_right)
7111       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7112     else if (can_turn_left)
7113       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7114     else if (can_turn_right)
7115       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7116     else
7117       MovDir[x][y] = back_dir;
7118
7119     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7120   }
7121   else if (move_pattern == MV_HORIZONTAL ||
7122            move_pattern == MV_VERTICAL)
7123   {
7124     if (move_pattern & old_move_dir)
7125       MovDir[x][y] = back_dir;
7126     else if (move_pattern == MV_HORIZONTAL)
7127       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7128     else if (move_pattern == MV_VERTICAL)
7129       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7130
7131     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7132   }
7133   else if (move_pattern & MV_ANY_DIRECTION)
7134   {
7135     MovDir[x][y] = move_pattern;
7136     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7137   }
7138   else if (move_pattern & MV_WIND_DIRECTION)
7139   {
7140     MovDir[x][y] = game.wind_direction;
7141     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7142   }
7143   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7144   {
7145     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7146       MovDir[x][y] = left_dir;
7147     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7148       MovDir[x][y] = right_dir;
7149
7150     if (MovDir[x][y] != old_move_dir)
7151       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7152   }
7153   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7154   {
7155     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7156       MovDir[x][y] = right_dir;
7157     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7158       MovDir[x][y] = left_dir;
7159
7160     if (MovDir[x][y] != old_move_dir)
7161       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7162   }
7163   else if (move_pattern == MV_TOWARDS_PLAYER ||
7164            move_pattern == MV_AWAY_FROM_PLAYER)
7165   {
7166     int attr_x = -1, attr_y = -1;
7167     int newx, newy;
7168     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7169
7170     if (AllPlayersGone)
7171     {
7172       attr_x = ExitX;
7173       attr_y = ExitY;
7174     }
7175     else
7176     {
7177       int i;
7178
7179       for (i = 0; i < MAX_PLAYERS; i++)
7180       {
7181         struct PlayerInfo *player = &stored_player[i];
7182         int jx = player->jx, jy = player->jy;
7183
7184         if (!player->active)
7185           continue;
7186
7187         if (attr_x == -1 ||
7188             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7189         {
7190           attr_x = jx;
7191           attr_y = jy;
7192         }
7193       }
7194     }
7195
7196     MovDir[x][y] = MV_NONE;
7197     if (attr_x < x)
7198       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7199     else if (attr_x > x)
7200       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7201     if (attr_y < y)
7202       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7203     else if (attr_y > y)
7204       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7205
7206     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7207
7208     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7209     {
7210       boolean first_horiz = RND(2);
7211       int new_move_dir = MovDir[x][y];
7212
7213       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7214       {
7215         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7216         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7217
7218         return;
7219       }
7220
7221       MovDir[x][y] =
7222         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7223       Moving2Blocked(x, y, &newx, &newy);
7224
7225       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7226         return;
7227
7228       MovDir[x][y] =
7229         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7230       Moving2Blocked(x, y, &newx, &newy);
7231
7232       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7233         return;
7234
7235       MovDir[x][y] = old_move_dir;
7236     }
7237   }
7238   else if (move_pattern == MV_WHEN_PUSHED ||
7239            move_pattern == MV_WHEN_DROPPED)
7240   {
7241     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7242       MovDir[x][y] = MV_NONE;
7243
7244     MovDelay[x][y] = 0;
7245   }
7246   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7247   {
7248     static int test_xy[7][2] =
7249     {
7250       { 0, -1 },
7251       { -1, 0 },
7252       { +1, 0 },
7253       { 0, +1 },
7254       { 0, -1 },
7255       { -1, 0 },
7256       { +1, 0 },
7257     };
7258     static int test_dir[7] =
7259     {
7260       MV_UP,
7261       MV_LEFT,
7262       MV_RIGHT,
7263       MV_DOWN,
7264       MV_UP,
7265       MV_LEFT,
7266       MV_RIGHT,
7267     };
7268     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7269     int move_preference = -1000000;     /* start with very low preference */
7270     int new_move_dir = MV_NONE;
7271     int start_test = RND(4);
7272     int i;
7273
7274     for (i = 0; i < NUM_DIRECTIONS; i++)
7275     {
7276       int move_dir = test_dir[start_test + i];
7277       int move_dir_preference;
7278
7279       xx = x + test_xy[start_test + i][0];
7280       yy = y + test_xy[start_test + i][1];
7281
7282       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7283           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7284       {
7285         new_move_dir = move_dir;
7286
7287         break;
7288       }
7289
7290       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7291         continue;
7292
7293       move_dir_preference = -1 * RunnerVisit[xx][yy];
7294       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7295         move_dir_preference = PlayerVisit[xx][yy];
7296
7297       if (move_dir_preference > move_preference)
7298       {
7299         /* prefer field that has not been visited for the longest time */
7300         move_preference = move_dir_preference;
7301         new_move_dir = move_dir;
7302       }
7303       else if (move_dir_preference == move_preference &&
7304                move_dir == old_move_dir)
7305       {
7306         /* prefer last direction when all directions are preferred equally */
7307         move_preference = move_dir_preference;
7308         new_move_dir = move_dir;
7309       }
7310     }
7311
7312     MovDir[x][y] = new_move_dir;
7313     if (old_move_dir != new_move_dir)
7314       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7315   }
7316 }
7317
7318 static void TurnRound(int x, int y)
7319 {
7320   int direction = MovDir[x][y];
7321
7322   TurnRoundExt(x, y);
7323
7324   GfxDir[x][y] = MovDir[x][y];
7325
7326   if (direction != MovDir[x][y])
7327     GfxFrame[x][y] = 0;
7328
7329   if (MovDelay[x][y])
7330     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7331
7332   ResetGfxFrame(x, y);
7333 }
7334
7335 static boolean JustBeingPushed(int x, int y)
7336 {
7337   int i;
7338
7339   for (i = 0; i < MAX_PLAYERS; i++)
7340   {
7341     struct PlayerInfo *player = &stored_player[i];
7342
7343     if (player->active && player->is_pushing && player->MovPos)
7344     {
7345       int next_jx = player->jx + (player->jx - player->last_jx);
7346       int next_jy = player->jy + (player->jy - player->last_jy);
7347
7348       if (x == next_jx && y == next_jy)
7349         return TRUE;
7350     }
7351   }
7352
7353   return FALSE;
7354 }
7355
7356 void StartMoving(int x, int y)
7357 {
7358   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7359   int element = Feld[x][y];
7360
7361   if (Stop[x][y])
7362     return;
7363
7364   if (MovDelay[x][y] == 0)
7365     GfxAction[x][y] = ACTION_DEFAULT;
7366
7367   if (CAN_FALL(element) && y < lev_fieldy - 1)
7368   {
7369     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7370         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7371       if (JustBeingPushed(x, y))
7372         return;
7373
7374     if (element == EL_QUICKSAND_FULL)
7375     {
7376       if (IS_FREE(x, y + 1))
7377       {
7378         InitMovingField(x, y, MV_DOWN);
7379         started_moving = TRUE;
7380
7381         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7382 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7383         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7384           Store[x][y] = EL_ROCK;
7385 #else
7386         Store[x][y] = EL_ROCK;
7387 #endif
7388
7389         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7390       }
7391       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7392       {
7393         if (!MovDelay[x][y])
7394         {
7395           MovDelay[x][y] = TILEY + 1;
7396
7397           ResetGfxAnimation(x, y);
7398           ResetGfxAnimation(x, y + 1);
7399         }
7400
7401         if (MovDelay[x][y])
7402         {
7403           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7404           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7405
7406           MovDelay[x][y]--;
7407           if (MovDelay[x][y])
7408             return;
7409         }
7410
7411         Feld[x][y] = EL_QUICKSAND_EMPTY;
7412         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7413         Store[x][y + 1] = Store[x][y];
7414         Store[x][y] = 0;
7415
7416         PlayLevelSoundAction(x, y, ACTION_FILLING);
7417       }
7418       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7419       {
7420         if (!MovDelay[x][y])
7421         {
7422           MovDelay[x][y] = TILEY + 1;
7423
7424           ResetGfxAnimation(x, y);
7425           ResetGfxAnimation(x, y + 1);
7426         }
7427
7428         if (MovDelay[x][y])
7429         {
7430           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7431           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7432
7433           MovDelay[x][y]--;
7434           if (MovDelay[x][y])
7435             return;
7436         }
7437
7438         Feld[x][y] = EL_QUICKSAND_EMPTY;
7439         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7440         Store[x][y + 1] = Store[x][y];
7441         Store[x][y] = 0;
7442
7443         PlayLevelSoundAction(x, y, ACTION_FILLING);
7444       }
7445     }
7446     else if (element == EL_QUICKSAND_FAST_FULL)
7447     {
7448       if (IS_FREE(x, y + 1))
7449       {
7450         InitMovingField(x, y, MV_DOWN);
7451         started_moving = TRUE;
7452
7453         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7454 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7455         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7456           Store[x][y] = EL_ROCK;
7457 #else
7458         Store[x][y] = EL_ROCK;
7459 #endif
7460
7461         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7462       }
7463       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7464       {
7465         if (!MovDelay[x][y])
7466         {
7467           MovDelay[x][y] = TILEY + 1;
7468
7469           ResetGfxAnimation(x, y);
7470           ResetGfxAnimation(x, y + 1);
7471         }
7472
7473         if (MovDelay[x][y])
7474         {
7475           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7476           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7477
7478           MovDelay[x][y]--;
7479           if (MovDelay[x][y])
7480             return;
7481         }
7482
7483         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7484         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7485         Store[x][y + 1] = Store[x][y];
7486         Store[x][y] = 0;
7487
7488         PlayLevelSoundAction(x, y, ACTION_FILLING);
7489       }
7490       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7491       {
7492         if (!MovDelay[x][y])
7493         {
7494           MovDelay[x][y] = TILEY + 1;
7495
7496           ResetGfxAnimation(x, y);
7497           ResetGfxAnimation(x, y + 1);
7498         }
7499
7500         if (MovDelay[x][y])
7501         {
7502           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7503           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7504
7505           MovDelay[x][y]--;
7506           if (MovDelay[x][y])
7507             return;
7508         }
7509
7510         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7511         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7512         Store[x][y + 1] = Store[x][y];
7513         Store[x][y] = 0;
7514
7515         PlayLevelSoundAction(x, y, ACTION_FILLING);
7516       }
7517     }
7518     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7519              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7520     {
7521       InitMovingField(x, y, MV_DOWN);
7522       started_moving = TRUE;
7523
7524       Feld[x][y] = EL_QUICKSAND_FILLING;
7525       Store[x][y] = element;
7526
7527       PlayLevelSoundAction(x, y, ACTION_FILLING);
7528     }
7529     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7530              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7531     {
7532       InitMovingField(x, y, MV_DOWN);
7533       started_moving = TRUE;
7534
7535       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7536       Store[x][y] = element;
7537
7538       PlayLevelSoundAction(x, y, ACTION_FILLING);
7539     }
7540     else if (element == EL_MAGIC_WALL_FULL)
7541     {
7542       if (IS_FREE(x, y + 1))
7543       {
7544         InitMovingField(x, y, MV_DOWN);
7545         started_moving = TRUE;
7546
7547         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7548         Store[x][y] = EL_CHANGED(Store[x][y]);
7549       }
7550       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7551       {
7552         if (!MovDelay[x][y])
7553           MovDelay[x][y] = TILEY / 4 + 1;
7554
7555         if (MovDelay[x][y])
7556         {
7557           MovDelay[x][y]--;
7558           if (MovDelay[x][y])
7559             return;
7560         }
7561
7562         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7563         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7564         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7565         Store[x][y] = 0;
7566       }
7567     }
7568     else if (element == EL_BD_MAGIC_WALL_FULL)
7569     {
7570       if (IS_FREE(x, y + 1))
7571       {
7572         InitMovingField(x, y, MV_DOWN);
7573         started_moving = TRUE;
7574
7575         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7576         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7577       }
7578       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7579       {
7580         if (!MovDelay[x][y])
7581           MovDelay[x][y] = TILEY / 4 + 1;
7582
7583         if (MovDelay[x][y])
7584         {
7585           MovDelay[x][y]--;
7586           if (MovDelay[x][y])
7587             return;
7588         }
7589
7590         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7591         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7592         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7593         Store[x][y] = 0;
7594       }
7595     }
7596     else if (element == EL_DC_MAGIC_WALL_FULL)
7597     {
7598       if (IS_FREE(x, y + 1))
7599       {
7600         InitMovingField(x, y, MV_DOWN);
7601         started_moving = TRUE;
7602
7603         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7604         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7605       }
7606       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7607       {
7608         if (!MovDelay[x][y])
7609           MovDelay[x][y] = TILEY / 4 + 1;
7610
7611         if (MovDelay[x][y])
7612         {
7613           MovDelay[x][y]--;
7614           if (MovDelay[x][y])
7615             return;
7616         }
7617
7618         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7619         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7620         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7621         Store[x][y] = 0;
7622       }
7623     }
7624     else if ((CAN_PASS_MAGIC_WALL(element) &&
7625               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7626                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7627              (CAN_PASS_DC_MAGIC_WALL(element) &&
7628               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7629
7630     {
7631       InitMovingField(x, y, MV_DOWN);
7632       started_moving = TRUE;
7633
7634       Feld[x][y] =
7635         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7636          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7637          EL_DC_MAGIC_WALL_FILLING);
7638       Store[x][y] = element;
7639     }
7640     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7641     {
7642       SplashAcid(x, y + 1);
7643
7644       InitMovingField(x, y, MV_DOWN);
7645       started_moving = TRUE;
7646
7647       Store[x][y] = EL_ACID;
7648     }
7649     else if (
7650              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7651               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7652              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7653               CAN_FALL(element) && WasJustFalling[x][y] &&
7654               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7655
7656              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7657               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7658               (Feld[x][y + 1] == EL_BLOCKED)))
7659     {
7660       /* this is needed for a special case not covered by calling "Impact()"
7661          from "ContinueMoving()": if an element moves to a tile directly below
7662          another element which was just falling on that tile (which was empty
7663          in the previous frame), the falling element above would just stop
7664          instead of smashing the element below (in previous version, the above
7665          element was just checked for "moving" instead of "falling", resulting
7666          in incorrect smashes caused by horizontal movement of the above
7667          element; also, the case of the player being the element to smash was
7668          simply not covered here... :-/ ) */
7669
7670       CheckCollision[x][y] = 0;
7671       CheckImpact[x][y] = 0;
7672
7673       Impact(x, y);
7674     }
7675     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7676     {
7677       if (MovDir[x][y] == MV_NONE)
7678       {
7679         InitMovingField(x, y, MV_DOWN);
7680         started_moving = TRUE;
7681       }
7682     }
7683     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7684     {
7685       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7686         MovDir[x][y] = MV_DOWN;
7687
7688       InitMovingField(x, y, MV_DOWN);
7689       started_moving = TRUE;
7690     }
7691     else if (element == EL_AMOEBA_DROP)
7692     {
7693       Feld[x][y] = EL_AMOEBA_GROWING;
7694       Store[x][y] = EL_AMOEBA_WET;
7695     }
7696     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7697               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7698              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7699              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7700     {
7701       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7702                                 (IS_FREE(x - 1, y + 1) ||
7703                                  Feld[x - 1][y + 1] == EL_ACID));
7704       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7705                                 (IS_FREE(x + 1, y + 1) ||
7706                                  Feld[x + 1][y + 1] == EL_ACID));
7707       boolean can_fall_any  = (can_fall_left || can_fall_right);
7708       boolean can_fall_both = (can_fall_left && can_fall_right);
7709       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7710
7711       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7712       {
7713         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7714           can_fall_right = FALSE;
7715         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7716           can_fall_left = FALSE;
7717         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7718           can_fall_right = FALSE;
7719         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7720           can_fall_left = FALSE;
7721
7722         can_fall_any  = (can_fall_left || can_fall_right);
7723         can_fall_both = FALSE;
7724       }
7725
7726       if (can_fall_both)
7727       {
7728         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7729           can_fall_right = FALSE;       /* slip down on left side */
7730         else
7731           can_fall_left = !(can_fall_right = RND(2));
7732
7733         can_fall_both = FALSE;
7734       }
7735
7736       if (can_fall_any)
7737       {
7738         /* if not determined otherwise, prefer left side for slipping down */
7739         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7740         started_moving = TRUE;
7741       }
7742     }
7743     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7744     {
7745       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7746       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7747       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7748       int belt_dir = game.belt_dir[belt_nr];
7749
7750       if ((belt_dir == MV_LEFT  && left_is_free) ||
7751           (belt_dir == MV_RIGHT && right_is_free))
7752       {
7753         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7754
7755         InitMovingField(x, y, belt_dir);
7756         started_moving = TRUE;
7757
7758         Pushed[x][y] = TRUE;
7759         Pushed[nextx][y] = TRUE;
7760
7761         GfxAction[x][y] = ACTION_DEFAULT;
7762       }
7763       else
7764       {
7765         MovDir[x][y] = 0;       /* if element was moving, stop it */
7766       }
7767     }
7768   }
7769
7770   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7771   if (CAN_MOVE(element) && !started_moving)
7772   {
7773     int move_pattern = element_info[element].move_pattern;
7774     int newx, newy;
7775
7776     Moving2Blocked(x, y, &newx, &newy);
7777
7778     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7779       return;
7780
7781     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7782         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7783     {
7784       WasJustMoving[x][y] = 0;
7785       CheckCollision[x][y] = 0;
7786
7787       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7788
7789       if (Feld[x][y] != element)        /* element has changed */
7790         return;
7791     }
7792
7793     if (!MovDelay[x][y])        /* start new movement phase */
7794     {
7795       /* all objects that can change their move direction after each step
7796          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7797
7798       if (element != EL_YAMYAM &&
7799           element != EL_DARK_YAMYAM &&
7800           element != EL_PACMAN &&
7801           !(move_pattern & MV_ANY_DIRECTION) &&
7802           move_pattern != MV_TURNING_LEFT &&
7803           move_pattern != MV_TURNING_RIGHT &&
7804           move_pattern != MV_TURNING_LEFT_RIGHT &&
7805           move_pattern != MV_TURNING_RIGHT_LEFT &&
7806           move_pattern != MV_TURNING_RANDOM)
7807       {
7808         TurnRound(x, y);
7809
7810         if (MovDelay[x][y] && (element == EL_BUG ||
7811                                element == EL_SPACESHIP ||
7812                                element == EL_SP_SNIKSNAK ||
7813                                element == EL_SP_ELECTRON ||
7814                                element == EL_MOLE))
7815           TEST_DrawLevelField(x, y);
7816       }
7817     }
7818
7819     if (MovDelay[x][y])         /* wait some time before next movement */
7820     {
7821       MovDelay[x][y]--;
7822
7823       if (element == EL_ROBOT ||
7824           element == EL_YAMYAM ||
7825           element == EL_DARK_YAMYAM)
7826       {
7827         DrawLevelElementAnimationIfNeeded(x, y, element);
7828         PlayLevelSoundAction(x, y, ACTION_WAITING);
7829       }
7830       else if (element == EL_SP_ELECTRON)
7831         DrawLevelElementAnimationIfNeeded(x, y, element);
7832       else if (element == EL_DRAGON)
7833       {
7834         int i;
7835         int dir = MovDir[x][y];
7836         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7837         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7838         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7839                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7840                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7841                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7842         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7843
7844         GfxAction[x][y] = ACTION_ATTACKING;
7845
7846         if (IS_PLAYER(x, y))
7847           DrawPlayerField(x, y);
7848         else
7849           TEST_DrawLevelField(x, y);
7850
7851         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7852
7853         for (i = 1; i <= 3; i++)
7854         {
7855           int xx = x + i * dx;
7856           int yy = y + i * dy;
7857           int sx = SCREENX(xx);
7858           int sy = SCREENY(yy);
7859           int flame_graphic = graphic + (i - 1);
7860
7861           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7862             break;
7863
7864           if (MovDelay[x][y])
7865           {
7866             int flamed = MovingOrBlocked2Element(xx, yy);
7867
7868             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7869               Bang(xx, yy);
7870             else
7871               RemoveMovingField(xx, yy);
7872
7873             ChangeDelay[xx][yy] = 0;
7874
7875             Feld[xx][yy] = EL_FLAMES;
7876
7877             if (IN_SCR_FIELD(sx, sy))
7878             {
7879               TEST_DrawLevelFieldCrumbled(xx, yy);
7880               DrawGraphic(sx, sy, flame_graphic, frame);
7881             }
7882           }
7883           else
7884           {
7885             if (Feld[xx][yy] == EL_FLAMES)
7886               Feld[xx][yy] = EL_EMPTY;
7887             TEST_DrawLevelField(xx, yy);
7888           }
7889         }
7890       }
7891
7892       if (MovDelay[x][y])       /* element still has to wait some time */
7893       {
7894         PlayLevelSoundAction(x, y, ACTION_WAITING);
7895
7896         return;
7897       }
7898     }
7899
7900     /* now make next step */
7901
7902     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7903
7904     if (DONT_COLLIDE_WITH(element) &&
7905         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7906         !PLAYER_ENEMY_PROTECTED(newx, newy))
7907     {
7908       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7909
7910       return;
7911     }
7912
7913     else if (CAN_MOVE_INTO_ACID(element) &&
7914              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7915              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7916              (MovDir[x][y] == MV_DOWN ||
7917               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7918     {
7919       SplashAcid(newx, newy);
7920       Store[x][y] = EL_ACID;
7921     }
7922     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7923     {
7924       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7925           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7926           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7927           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7928       {
7929         RemoveField(x, y);
7930         TEST_DrawLevelField(x, y);
7931
7932         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7933         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7934           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7935
7936         local_player->friends_still_needed--;
7937         if (!local_player->friends_still_needed &&
7938             !local_player->GameOver && AllPlayersGone)
7939           PlayerWins(local_player);
7940
7941         return;
7942       }
7943       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7944       {
7945         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7946           TEST_DrawLevelField(newx, newy);
7947         else
7948           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7949       }
7950       else if (!IS_FREE(newx, newy))
7951       {
7952         GfxAction[x][y] = ACTION_WAITING;
7953
7954         if (IS_PLAYER(x, y))
7955           DrawPlayerField(x, y);
7956         else
7957           TEST_DrawLevelField(x, y);
7958
7959         return;
7960       }
7961     }
7962     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7963     {
7964       if (IS_FOOD_PIG(Feld[newx][newy]))
7965       {
7966         if (IS_MOVING(newx, newy))
7967           RemoveMovingField(newx, newy);
7968         else
7969         {
7970           Feld[newx][newy] = EL_EMPTY;
7971           TEST_DrawLevelField(newx, newy);
7972         }
7973
7974         PlayLevelSound(x, y, SND_PIG_DIGGING);
7975       }
7976       else if (!IS_FREE(newx, newy))
7977       {
7978         if (IS_PLAYER(x, y))
7979           DrawPlayerField(x, y);
7980         else
7981           TEST_DrawLevelField(x, y);
7982
7983         return;
7984       }
7985     }
7986     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7987     {
7988       if (Store[x][y] != EL_EMPTY)
7989       {
7990         boolean can_clone = FALSE;
7991         int xx, yy;
7992
7993         /* check if element to clone is still there */
7994         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7995         {
7996           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7997           {
7998             can_clone = TRUE;
7999
8000             break;
8001           }
8002         }
8003
8004         /* cannot clone or target field not free anymore -- do not clone */
8005         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8006           Store[x][y] = EL_EMPTY;
8007       }
8008
8009       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8010       {
8011         if (IS_MV_DIAGONAL(MovDir[x][y]))
8012         {
8013           int diagonal_move_dir = MovDir[x][y];
8014           int stored = Store[x][y];
8015           int change_delay = 8;
8016           int graphic;
8017
8018           /* android is moving diagonally */
8019
8020           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8021
8022           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8023           GfxElement[x][y] = EL_EMC_ANDROID;
8024           GfxAction[x][y] = ACTION_SHRINKING;
8025           GfxDir[x][y] = diagonal_move_dir;
8026           ChangeDelay[x][y] = change_delay;
8027
8028           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8029                                    GfxDir[x][y]);
8030
8031           DrawLevelGraphicAnimation(x, y, graphic);
8032           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8033
8034           if (Feld[newx][newy] == EL_ACID)
8035           {
8036             SplashAcid(newx, newy);
8037
8038             return;
8039           }
8040
8041           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8042
8043           Store[newx][newy] = EL_EMC_ANDROID;
8044           GfxElement[newx][newy] = EL_EMC_ANDROID;
8045           GfxAction[newx][newy] = ACTION_GROWING;
8046           GfxDir[newx][newy] = diagonal_move_dir;
8047           ChangeDelay[newx][newy] = change_delay;
8048
8049           graphic = el_act_dir2img(GfxElement[newx][newy],
8050                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8051
8052           DrawLevelGraphicAnimation(newx, newy, graphic);
8053           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8054
8055           return;
8056         }
8057         else
8058         {
8059           Feld[newx][newy] = EL_EMPTY;
8060           TEST_DrawLevelField(newx, newy);
8061
8062           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8063         }
8064       }
8065       else if (!IS_FREE(newx, newy))
8066       {
8067         return;
8068       }
8069     }
8070     else if (IS_CUSTOM_ELEMENT(element) &&
8071              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8072     {
8073       if (!DigFieldByCE(newx, newy, element))
8074         return;
8075
8076       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8077       {
8078         RunnerVisit[x][y] = FrameCounter;
8079         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8080       }
8081     }
8082     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8083     {
8084       if (!IS_FREE(newx, newy))
8085       {
8086         if (IS_PLAYER(x, y))
8087           DrawPlayerField(x, y);
8088         else
8089           TEST_DrawLevelField(x, y);
8090
8091         return;
8092       }
8093       else
8094       {
8095         boolean wanna_flame = !RND(10);
8096         int dx = newx - x, dy = newy - y;
8097         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8098         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8099         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8100                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8101         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8102                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8103
8104         if ((wanna_flame ||
8105              IS_CLASSIC_ENEMY(element1) ||
8106              IS_CLASSIC_ENEMY(element2)) &&
8107             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8108             element1 != EL_FLAMES && element2 != EL_FLAMES)
8109         {
8110           ResetGfxAnimation(x, y);
8111           GfxAction[x][y] = ACTION_ATTACKING;
8112
8113           if (IS_PLAYER(x, y))
8114             DrawPlayerField(x, y);
8115           else
8116             TEST_DrawLevelField(x, y);
8117
8118           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8119
8120           MovDelay[x][y] = 50;
8121
8122           Feld[newx][newy] = EL_FLAMES;
8123           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8124             Feld[newx1][newy1] = EL_FLAMES;
8125           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8126             Feld[newx2][newy2] = EL_FLAMES;
8127
8128           return;
8129         }
8130       }
8131     }
8132     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8133              Feld[newx][newy] == EL_DIAMOND)
8134     {
8135       if (IS_MOVING(newx, newy))
8136         RemoveMovingField(newx, newy);
8137       else
8138       {
8139         Feld[newx][newy] = EL_EMPTY;
8140         TEST_DrawLevelField(newx, newy);
8141       }
8142
8143       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8144     }
8145     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8146              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8147     {
8148       if (AmoebaNr[newx][newy])
8149       {
8150         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8151         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8152             Feld[newx][newy] == EL_BD_AMOEBA)
8153           AmoebaCnt[AmoebaNr[newx][newy]]--;
8154       }
8155
8156       if (IS_MOVING(newx, newy))
8157       {
8158         RemoveMovingField(newx, newy);
8159       }
8160       else
8161       {
8162         Feld[newx][newy] = EL_EMPTY;
8163         TEST_DrawLevelField(newx, newy);
8164       }
8165
8166       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8167     }
8168     else if ((element == EL_PACMAN || element == EL_MOLE)
8169              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8170     {
8171       if (AmoebaNr[newx][newy])
8172       {
8173         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8174         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8175             Feld[newx][newy] == EL_BD_AMOEBA)
8176           AmoebaCnt[AmoebaNr[newx][newy]]--;
8177       }
8178
8179       if (element == EL_MOLE)
8180       {
8181         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8182         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8183
8184         ResetGfxAnimation(x, y);
8185         GfxAction[x][y] = ACTION_DIGGING;
8186         TEST_DrawLevelField(x, y);
8187
8188         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8189
8190         return;                         /* wait for shrinking amoeba */
8191       }
8192       else      /* element == EL_PACMAN */
8193       {
8194         Feld[newx][newy] = EL_EMPTY;
8195         TEST_DrawLevelField(newx, newy);
8196         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8197       }
8198     }
8199     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8200              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8201               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8202     {
8203       /* wait for shrinking amoeba to completely disappear */
8204       return;
8205     }
8206     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8207     {
8208       /* object was running against a wall */
8209
8210       TurnRound(x, y);
8211
8212       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8213         DrawLevelElementAnimation(x, y, element);
8214
8215       if (DONT_TOUCH(element))
8216         TestIfBadThingTouchesPlayer(x, y);
8217
8218       return;
8219     }
8220
8221     InitMovingField(x, y, MovDir[x][y]);
8222
8223     PlayLevelSoundAction(x, y, ACTION_MOVING);
8224   }
8225
8226   if (MovDir[x][y])
8227     ContinueMoving(x, y);
8228 }
8229
8230 void ContinueMoving(int x, int y)
8231 {
8232   int element = Feld[x][y];
8233   struct ElementInfo *ei = &element_info[element];
8234   int direction = MovDir[x][y];
8235   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8236   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8237   int newx = x + dx, newy = y + dy;
8238   int stored = Store[x][y];
8239   int stored_new = Store[newx][newy];
8240   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8241   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8242   boolean last_line = (newy == lev_fieldy - 1);
8243
8244   MovPos[x][y] += getElementMoveStepsize(x, y);
8245
8246   if (pushed_by_player) /* special case: moving object pushed by player */
8247     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8248
8249   if (ABS(MovPos[x][y]) < TILEX)
8250   {
8251     TEST_DrawLevelField(x, y);
8252
8253     return;     /* element is still moving */
8254   }
8255
8256   /* element reached destination field */
8257
8258   Feld[x][y] = EL_EMPTY;
8259   Feld[newx][newy] = element;
8260   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8261
8262   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8263   {
8264     element = Feld[newx][newy] = EL_ACID;
8265   }
8266   else if (element == EL_MOLE)
8267   {
8268     Feld[x][y] = EL_SAND;
8269
8270     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8271   }
8272   else if (element == EL_QUICKSAND_FILLING)
8273   {
8274     element = Feld[newx][newy] = get_next_element(element);
8275     Store[newx][newy] = Store[x][y];
8276   }
8277   else if (element == EL_QUICKSAND_EMPTYING)
8278   {
8279     Feld[x][y] = get_next_element(element);
8280     element = Feld[newx][newy] = Store[x][y];
8281   }
8282   else if (element == EL_QUICKSAND_FAST_FILLING)
8283   {
8284     element = Feld[newx][newy] = get_next_element(element);
8285     Store[newx][newy] = Store[x][y];
8286   }
8287   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8288   {
8289     Feld[x][y] = get_next_element(element);
8290     element = Feld[newx][newy] = Store[x][y];
8291   }
8292   else if (element == EL_MAGIC_WALL_FILLING)
8293   {
8294     element = Feld[newx][newy] = get_next_element(element);
8295     if (!game.magic_wall_active)
8296       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8297     Store[newx][newy] = Store[x][y];
8298   }
8299   else if (element == EL_MAGIC_WALL_EMPTYING)
8300   {
8301     Feld[x][y] = get_next_element(element);
8302     if (!game.magic_wall_active)
8303       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8304     element = Feld[newx][newy] = Store[x][y];
8305
8306     InitField(newx, newy, FALSE);
8307   }
8308   else if (element == EL_BD_MAGIC_WALL_FILLING)
8309   {
8310     element = Feld[newx][newy] = get_next_element(element);
8311     if (!game.magic_wall_active)
8312       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8313     Store[newx][newy] = Store[x][y];
8314   }
8315   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8316   {
8317     Feld[x][y] = get_next_element(element);
8318     if (!game.magic_wall_active)
8319       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8320     element = Feld[newx][newy] = Store[x][y];
8321
8322     InitField(newx, newy, FALSE);
8323   }
8324   else if (element == EL_DC_MAGIC_WALL_FILLING)
8325   {
8326     element = Feld[newx][newy] = get_next_element(element);
8327     if (!game.magic_wall_active)
8328       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8329     Store[newx][newy] = Store[x][y];
8330   }
8331   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8332   {
8333     Feld[x][y] = get_next_element(element);
8334     if (!game.magic_wall_active)
8335       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8336     element = Feld[newx][newy] = Store[x][y];
8337
8338     InitField(newx, newy, FALSE);
8339   }
8340   else if (element == EL_AMOEBA_DROPPING)
8341   {
8342     Feld[x][y] = get_next_element(element);
8343     element = Feld[newx][newy] = Store[x][y];
8344   }
8345   else if (element == EL_SOKOBAN_OBJECT)
8346   {
8347     if (Back[x][y])
8348       Feld[x][y] = Back[x][y];
8349
8350     if (Back[newx][newy])
8351       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8352
8353     Back[x][y] = Back[newx][newy] = 0;
8354   }
8355
8356   Store[x][y] = EL_EMPTY;
8357   MovPos[x][y] = 0;
8358   MovDir[x][y] = 0;
8359   MovDelay[x][y] = 0;
8360
8361   MovDelay[newx][newy] = 0;
8362
8363   if (CAN_CHANGE_OR_HAS_ACTION(element))
8364   {
8365     /* copy element change control values to new field */
8366     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8367     ChangePage[newx][newy]  = ChangePage[x][y];
8368     ChangeCount[newx][newy] = ChangeCount[x][y];
8369     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8370   }
8371
8372   CustomValue[newx][newy] = CustomValue[x][y];
8373
8374   ChangeDelay[x][y] = 0;
8375   ChangePage[x][y] = -1;
8376   ChangeCount[x][y] = 0;
8377   ChangeEvent[x][y] = -1;
8378
8379   CustomValue[x][y] = 0;
8380
8381   /* copy animation control values to new field */
8382   GfxFrame[newx][newy]  = GfxFrame[x][y];
8383   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8384   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8385   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8386
8387   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8388
8389   /* some elements can leave other elements behind after moving */
8390   if (ei->move_leave_element != EL_EMPTY &&
8391       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8392       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8393   {
8394     int move_leave_element = ei->move_leave_element;
8395
8396     /* this makes it possible to leave the removed element again */
8397     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8398       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8399
8400     Feld[x][y] = move_leave_element;
8401
8402     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8403       MovDir[x][y] = direction;
8404
8405     InitField(x, y, FALSE);
8406
8407     if (GFX_CRUMBLED(Feld[x][y]))
8408       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8409
8410     if (ELEM_IS_PLAYER(move_leave_element))
8411       RelocatePlayer(x, y, move_leave_element);
8412   }
8413
8414   /* do this after checking for left-behind element */
8415   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8416
8417   if (!CAN_MOVE(element) ||
8418       (CAN_FALL(element) && direction == MV_DOWN &&
8419        (element == EL_SPRING ||
8420         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8421         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8422     GfxDir[x][y] = MovDir[newx][newy] = 0;
8423
8424   TEST_DrawLevelField(x, y);
8425   TEST_DrawLevelField(newx, newy);
8426
8427   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8428
8429   /* prevent pushed element from moving on in pushed direction */
8430   if (pushed_by_player && CAN_MOVE(element) &&
8431       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8432       !(element_info[element].move_pattern & direction))
8433     TurnRound(newx, newy);
8434
8435   /* prevent elements on conveyor belt from moving on in last direction */
8436   if (pushed_by_conveyor && CAN_FALL(element) &&
8437       direction & MV_HORIZONTAL)
8438     MovDir[newx][newy] = 0;
8439
8440   if (!pushed_by_player)
8441   {
8442     int nextx = newx + dx, nexty = newy + dy;
8443     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8444
8445     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8446
8447     if (CAN_FALL(element) && direction == MV_DOWN)
8448       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8449
8450     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8451       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8452
8453     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8454       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8455   }
8456
8457   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8458   {
8459     TestIfBadThingTouchesPlayer(newx, newy);
8460     TestIfBadThingTouchesFriend(newx, newy);
8461
8462     if (!IS_CUSTOM_ELEMENT(element))
8463       TestIfBadThingTouchesOtherBadThing(newx, newy);
8464   }
8465   else if (element == EL_PENGUIN)
8466     TestIfFriendTouchesBadThing(newx, newy);
8467
8468   if (DONT_GET_HIT_BY(element))
8469   {
8470     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8471   }
8472
8473   /* give the player one last chance (one more frame) to move away */
8474   if (CAN_FALL(element) && direction == MV_DOWN &&
8475       (last_line || (!IS_FREE(x, newy + 1) &&
8476                      (!IS_PLAYER(x, newy + 1) ||
8477                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8478     Impact(x, newy);
8479
8480   if (pushed_by_player && !game.use_change_when_pushing_bug)
8481   {
8482     int push_side = MV_DIR_OPPOSITE(direction);
8483     struct PlayerInfo *player = PLAYERINFO(x, y);
8484
8485     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8486                                player->index_bit, push_side);
8487     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8488                                         player->index_bit, push_side);
8489   }
8490
8491   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8492     MovDelay[newx][newy] = 1;
8493
8494   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8495
8496   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8497   TestIfElementHitsCustomElement(newx, newy, direction);
8498   TestIfPlayerTouchesCustomElement(newx, newy);
8499   TestIfElementTouchesCustomElement(newx, newy);
8500
8501   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8502       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8503     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8504                              MV_DIR_OPPOSITE(direction));
8505 }
8506
8507 int AmoebeNachbarNr(int ax, int ay)
8508 {
8509   int i;
8510   int element = Feld[ax][ay];
8511   int group_nr = 0;
8512   static int xy[4][2] =
8513   {
8514     { 0, -1 },
8515     { -1, 0 },
8516     { +1, 0 },
8517     { 0, +1 }
8518   };
8519
8520   for (i = 0; i < NUM_DIRECTIONS; i++)
8521   {
8522     int x = ax + xy[i][0];
8523     int y = ay + xy[i][1];
8524
8525     if (!IN_LEV_FIELD(x, y))
8526       continue;
8527
8528     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8529       group_nr = AmoebaNr[x][y];
8530   }
8531
8532   return group_nr;
8533 }
8534
8535 void AmoebenVereinigen(int ax, int ay)
8536 {
8537   int i, x, y, xx, yy;
8538   int new_group_nr = AmoebaNr[ax][ay];
8539   static int xy[4][2] =
8540   {
8541     { 0, -1 },
8542     { -1, 0 },
8543     { +1, 0 },
8544     { 0, +1 }
8545   };
8546
8547   if (new_group_nr == 0)
8548     return;
8549
8550   for (i = 0; i < NUM_DIRECTIONS; i++)
8551   {
8552     x = ax + xy[i][0];
8553     y = ay + xy[i][1];
8554
8555     if (!IN_LEV_FIELD(x, y))
8556       continue;
8557
8558     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8559          Feld[x][y] == EL_BD_AMOEBA ||
8560          Feld[x][y] == EL_AMOEBA_DEAD) &&
8561         AmoebaNr[x][y] != new_group_nr)
8562     {
8563       int old_group_nr = AmoebaNr[x][y];
8564
8565       if (old_group_nr == 0)
8566         return;
8567
8568       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8569       AmoebaCnt[old_group_nr] = 0;
8570       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8571       AmoebaCnt2[old_group_nr] = 0;
8572
8573       SCAN_PLAYFIELD(xx, yy)
8574       {
8575         if (AmoebaNr[xx][yy] == old_group_nr)
8576           AmoebaNr[xx][yy] = new_group_nr;
8577       }
8578     }
8579   }
8580 }
8581
8582 void AmoebeUmwandeln(int ax, int ay)
8583 {
8584   int i, x, y;
8585
8586   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8587   {
8588     int group_nr = AmoebaNr[ax][ay];
8589
8590 #ifdef DEBUG
8591     if (group_nr == 0)
8592     {
8593       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8594       printf("AmoebeUmwandeln(): This should never happen!\n");
8595       return;
8596     }
8597 #endif
8598
8599     SCAN_PLAYFIELD(x, y)
8600     {
8601       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8602       {
8603         AmoebaNr[x][y] = 0;
8604         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8605       }
8606     }
8607
8608     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8609                             SND_AMOEBA_TURNING_TO_GEM :
8610                             SND_AMOEBA_TURNING_TO_ROCK));
8611     Bang(ax, ay);
8612   }
8613   else
8614   {
8615     static int xy[4][2] =
8616     {
8617       { 0, -1 },
8618       { -1, 0 },
8619       { +1, 0 },
8620       { 0, +1 }
8621     };
8622
8623     for (i = 0; i < NUM_DIRECTIONS; i++)
8624     {
8625       x = ax + xy[i][0];
8626       y = ay + xy[i][1];
8627
8628       if (!IN_LEV_FIELD(x, y))
8629         continue;
8630
8631       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8632       {
8633         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8634                               SND_AMOEBA_TURNING_TO_GEM :
8635                               SND_AMOEBA_TURNING_TO_ROCK));
8636         Bang(x, y);
8637       }
8638     }
8639   }
8640 }
8641
8642 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8643 {
8644   int x, y;
8645   int group_nr = AmoebaNr[ax][ay];
8646   boolean done = FALSE;
8647
8648 #ifdef DEBUG
8649   if (group_nr == 0)
8650   {
8651     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8652     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8653     return;
8654   }
8655 #endif
8656
8657   SCAN_PLAYFIELD(x, y)
8658   {
8659     if (AmoebaNr[x][y] == group_nr &&
8660         (Feld[x][y] == EL_AMOEBA_DEAD ||
8661          Feld[x][y] == EL_BD_AMOEBA ||
8662          Feld[x][y] == EL_AMOEBA_GROWING))
8663     {
8664       AmoebaNr[x][y] = 0;
8665       Feld[x][y] = new_element;
8666       InitField(x, y, FALSE);
8667       TEST_DrawLevelField(x, y);
8668       done = TRUE;
8669     }
8670   }
8671
8672   if (done)
8673     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8674                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8675                             SND_BD_AMOEBA_TURNING_TO_GEM));
8676 }
8677
8678 void AmoebeWaechst(int x, int y)
8679 {
8680   static unsigned int sound_delay = 0;
8681   static unsigned int sound_delay_value = 0;
8682
8683   if (!MovDelay[x][y])          /* start new growing cycle */
8684   {
8685     MovDelay[x][y] = 7;
8686
8687     if (DelayReached(&sound_delay, sound_delay_value))
8688     {
8689       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8690       sound_delay_value = 30;
8691     }
8692   }
8693
8694   if (MovDelay[x][y])           /* wait some time before growing bigger */
8695   {
8696     MovDelay[x][y]--;
8697     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8698     {
8699       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8700                                            6 - MovDelay[x][y]);
8701
8702       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8703     }
8704
8705     if (!MovDelay[x][y])
8706     {
8707       Feld[x][y] = Store[x][y];
8708       Store[x][y] = 0;
8709       TEST_DrawLevelField(x, y);
8710     }
8711   }
8712 }
8713
8714 void AmoebaDisappearing(int x, int y)
8715 {
8716   static unsigned int sound_delay = 0;
8717   static unsigned int sound_delay_value = 0;
8718
8719   if (!MovDelay[x][y])          /* start new shrinking cycle */
8720   {
8721     MovDelay[x][y] = 7;
8722
8723     if (DelayReached(&sound_delay, sound_delay_value))
8724       sound_delay_value = 30;
8725   }
8726
8727   if (MovDelay[x][y])           /* wait some time before shrinking */
8728   {
8729     MovDelay[x][y]--;
8730     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8731     {
8732       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8733                                            6 - MovDelay[x][y]);
8734
8735       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8736     }
8737
8738     if (!MovDelay[x][y])
8739     {
8740       Feld[x][y] = EL_EMPTY;
8741       TEST_DrawLevelField(x, y);
8742
8743       /* don't let mole enter this field in this cycle;
8744          (give priority to objects falling to this field from above) */
8745       Stop[x][y] = TRUE;
8746     }
8747   }
8748 }
8749
8750 void AmoebeAbleger(int ax, int ay)
8751 {
8752   int i;
8753   int element = Feld[ax][ay];
8754   int graphic = el2img(element);
8755   int newax = ax, neway = ay;
8756   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8757   static int xy[4][2] =
8758   {
8759     { 0, -1 },
8760     { -1, 0 },
8761     { +1, 0 },
8762     { 0, +1 }
8763   };
8764
8765   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8766   {
8767     Feld[ax][ay] = EL_AMOEBA_DEAD;
8768     TEST_DrawLevelField(ax, ay);
8769     return;
8770   }
8771
8772   if (IS_ANIMATED(graphic))
8773     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8774
8775   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8776     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8777
8778   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8779   {
8780     MovDelay[ax][ay]--;
8781     if (MovDelay[ax][ay])
8782       return;
8783   }
8784
8785   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8786   {
8787     int start = RND(4);
8788     int x = ax + xy[start][0];
8789     int y = ay + xy[start][1];
8790
8791     if (!IN_LEV_FIELD(x, y))
8792       return;
8793
8794     if (IS_FREE(x, y) ||
8795         CAN_GROW_INTO(Feld[x][y]) ||
8796         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8797         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8798     {
8799       newax = x;
8800       neway = y;
8801     }
8802
8803     if (newax == ax && neway == ay)
8804       return;
8805   }
8806   else                          /* normal or "filled" (BD style) amoeba */
8807   {
8808     int start = RND(4);
8809     boolean waiting_for_player = FALSE;
8810
8811     for (i = 0; i < NUM_DIRECTIONS; i++)
8812     {
8813       int j = (start + i) % 4;
8814       int x = ax + xy[j][0];
8815       int y = ay + xy[j][1];
8816
8817       if (!IN_LEV_FIELD(x, y))
8818         continue;
8819
8820       if (IS_FREE(x, y) ||
8821           CAN_GROW_INTO(Feld[x][y]) ||
8822           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8823           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8824       {
8825         newax = x;
8826         neway = y;
8827         break;
8828       }
8829       else if (IS_PLAYER(x, y))
8830         waiting_for_player = TRUE;
8831     }
8832
8833     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8834     {
8835       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8836       {
8837         Feld[ax][ay] = EL_AMOEBA_DEAD;
8838         TEST_DrawLevelField(ax, ay);
8839         AmoebaCnt[AmoebaNr[ax][ay]]--;
8840
8841         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8842         {
8843           if (element == EL_AMOEBA_FULL)
8844             AmoebeUmwandeln(ax, ay);
8845           else if (element == EL_BD_AMOEBA)
8846             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8847         }
8848       }
8849       return;
8850     }
8851     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8852     {
8853       /* amoeba gets larger by growing in some direction */
8854
8855       int new_group_nr = AmoebaNr[ax][ay];
8856
8857 #ifdef DEBUG
8858   if (new_group_nr == 0)
8859   {
8860     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8861     printf("AmoebeAbleger(): This should never happen!\n");
8862     return;
8863   }
8864 #endif
8865
8866       AmoebaNr[newax][neway] = new_group_nr;
8867       AmoebaCnt[new_group_nr]++;
8868       AmoebaCnt2[new_group_nr]++;
8869
8870       /* if amoeba touches other amoeba(s) after growing, unify them */
8871       AmoebenVereinigen(newax, neway);
8872
8873       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8874       {
8875         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8876         return;
8877       }
8878     }
8879   }
8880
8881   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8882       (neway == lev_fieldy - 1 && newax != ax))
8883   {
8884     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8885     Store[newax][neway] = element;
8886   }
8887   else if (neway == ay || element == EL_EMC_DRIPPER)
8888   {
8889     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8890
8891     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8892   }
8893   else
8894   {
8895     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8896     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8897     Store[ax][ay] = EL_AMOEBA_DROP;
8898     ContinueMoving(ax, ay);
8899     return;
8900   }
8901
8902   TEST_DrawLevelField(newax, neway);
8903 }
8904
8905 void Life(int ax, int ay)
8906 {
8907   int x1, y1, x2, y2;
8908   int life_time = 40;
8909   int element = Feld[ax][ay];
8910   int graphic = el2img(element);
8911   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8912                          level.biomaze);
8913   boolean changed = FALSE;
8914
8915   if (IS_ANIMATED(graphic))
8916     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8917
8918   if (Stop[ax][ay])
8919     return;
8920
8921   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8922     MovDelay[ax][ay] = life_time;
8923
8924   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8925   {
8926     MovDelay[ax][ay]--;
8927     if (MovDelay[ax][ay])
8928       return;
8929   }
8930
8931   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8932   {
8933     int xx = ax+x1, yy = ay+y1;
8934     int nachbarn = 0;
8935
8936     if (!IN_LEV_FIELD(xx, yy))
8937       continue;
8938
8939     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8940     {
8941       int x = xx+x2, y = yy+y2;
8942
8943       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8944         continue;
8945
8946       if (((Feld[x][y] == element ||
8947             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8948            !Stop[x][y]) ||
8949           (IS_FREE(x, y) && Stop[x][y]))
8950         nachbarn++;
8951     }
8952
8953     if (xx == ax && yy == ay)           /* field in the middle */
8954     {
8955       if (nachbarn < life_parameter[0] ||
8956           nachbarn > life_parameter[1])
8957       {
8958         Feld[xx][yy] = EL_EMPTY;
8959         if (!Stop[xx][yy])
8960           TEST_DrawLevelField(xx, yy);
8961         Stop[xx][yy] = TRUE;
8962         changed = TRUE;
8963       }
8964     }
8965     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8966     {                                   /* free border field */
8967       if (nachbarn >= life_parameter[2] &&
8968           nachbarn <= life_parameter[3])
8969       {
8970         Feld[xx][yy] = element;
8971         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8972         if (!Stop[xx][yy])
8973           TEST_DrawLevelField(xx, yy);
8974         Stop[xx][yy] = TRUE;
8975         changed = TRUE;
8976       }
8977     }
8978   }
8979
8980   if (changed)
8981     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8982                    SND_GAME_OF_LIFE_GROWING);
8983 }
8984
8985 static void InitRobotWheel(int x, int y)
8986 {
8987   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8988 }
8989
8990 static void RunRobotWheel(int x, int y)
8991 {
8992   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8993 }
8994
8995 static void StopRobotWheel(int x, int y)
8996 {
8997   if (ZX == x && ZY == y)
8998   {
8999     ZX = ZY = -1;
9000
9001     game.robot_wheel_active = FALSE;
9002   }
9003 }
9004
9005 static void InitTimegateWheel(int x, int y)
9006 {
9007   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9008 }
9009
9010 static void RunTimegateWheel(int x, int y)
9011 {
9012   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9013 }
9014
9015 static void InitMagicBallDelay(int x, int y)
9016 {
9017   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9018 }
9019
9020 static void ActivateMagicBall(int bx, int by)
9021 {
9022   int x, y;
9023
9024   if (level.ball_random)
9025   {
9026     int pos_border = RND(8);    /* select one of the eight border elements */
9027     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9028     int xx = pos_content % 3;
9029     int yy = pos_content / 3;
9030
9031     x = bx - 1 + xx;
9032     y = by - 1 + yy;
9033
9034     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9035       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9036   }
9037   else
9038   {
9039     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9040     {
9041       int xx = x - bx + 1;
9042       int yy = y - by + 1;
9043
9044       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9045         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9046     }
9047   }
9048
9049   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9050 }
9051
9052 void CheckExit(int x, int y)
9053 {
9054   if (local_player->gems_still_needed > 0 ||
9055       local_player->sokobanfields_still_needed > 0 ||
9056       local_player->lights_still_needed > 0)
9057   {
9058     int element = Feld[x][y];
9059     int graphic = el2img(element);
9060
9061     if (IS_ANIMATED(graphic))
9062       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9063
9064     return;
9065   }
9066
9067   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9068     return;
9069
9070   Feld[x][y] = EL_EXIT_OPENING;
9071
9072   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9073 }
9074
9075 void CheckExitEM(int x, int y)
9076 {
9077   if (local_player->gems_still_needed > 0 ||
9078       local_player->sokobanfields_still_needed > 0 ||
9079       local_player->lights_still_needed > 0)
9080   {
9081     int element = Feld[x][y];
9082     int graphic = el2img(element);
9083
9084     if (IS_ANIMATED(graphic))
9085       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9086
9087     return;
9088   }
9089
9090   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9091     return;
9092
9093   Feld[x][y] = EL_EM_EXIT_OPENING;
9094
9095   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9096 }
9097
9098 void CheckExitSteel(int x, int y)
9099 {
9100   if (local_player->gems_still_needed > 0 ||
9101       local_player->sokobanfields_still_needed > 0 ||
9102       local_player->lights_still_needed > 0)
9103   {
9104     int element = Feld[x][y];
9105     int graphic = el2img(element);
9106
9107     if (IS_ANIMATED(graphic))
9108       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9109
9110     return;
9111   }
9112
9113   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9114     return;
9115
9116   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9117
9118   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9119 }
9120
9121 void CheckExitSteelEM(int x, int y)
9122 {
9123   if (local_player->gems_still_needed > 0 ||
9124       local_player->sokobanfields_still_needed > 0 ||
9125       local_player->lights_still_needed > 0)
9126   {
9127     int element = Feld[x][y];
9128     int graphic = el2img(element);
9129
9130     if (IS_ANIMATED(graphic))
9131       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9132
9133     return;
9134   }
9135
9136   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9137     return;
9138
9139   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9140
9141   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9142 }
9143
9144 void CheckExitSP(int x, int y)
9145 {
9146   if (local_player->gems_still_needed > 0)
9147   {
9148     int element = Feld[x][y];
9149     int graphic = el2img(element);
9150
9151     if (IS_ANIMATED(graphic))
9152       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9153
9154     return;
9155   }
9156
9157   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9158     return;
9159
9160   Feld[x][y] = EL_SP_EXIT_OPENING;
9161
9162   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9163 }
9164
9165 static void CloseAllOpenTimegates()
9166 {
9167   int x, y;
9168
9169   SCAN_PLAYFIELD(x, y)
9170   {
9171     int element = Feld[x][y];
9172
9173     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9174     {
9175       Feld[x][y] = EL_TIMEGATE_CLOSING;
9176
9177       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9178     }
9179   }
9180 }
9181
9182 void DrawTwinkleOnField(int x, int y)
9183 {
9184   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9185     return;
9186
9187   if (Feld[x][y] == EL_BD_DIAMOND)
9188     return;
9189
9190   if (MovDelay[x][y] == 0)      /* next animation frame */
9191     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9192
9193   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9194   {
9195     MovDelay[x][y]--;
9196
9197     DrawLevelElementAnimation(x, y, Feld[x][y]);
9198
9199     if (MovDelay[x][y] != 0)
9200     {
9201       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9202                                            10 - MovDelay[x][y]);
9203
9204       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9205     }
9206   }
9207 }
9208
9209 void MauerWaechst(int x, int y)
9210 {
9211   int delay = 6;
9212
9213   if (!MovDelay[x][y])          /* next animation frame */
9214     MovDelay[x][y] = 3 * delay;
9215
9216   if (MovDelay[x][y])           /* wait some time before next frame */
9217   {
9218     MovDelay[x][y]--;
9219
9220     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9221     {
9222       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9223       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9224
9225       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9226     }
9227
9228     if (!MovDelay[x][y])
9229     {
9230       if (MovDir[x][y] == MV_LEFT)
9231       {
9232         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9233           TEST_DrawLevelField(x - 1, y);
9234       }
9235       else if (MovDir[x][y] == MV_RIGHT)
9236       {
9237         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9238           TEST_DrawLevelField(x + 1, y);
9239       }
9240       else if (MovDir[x][y] == MV_UP)
9241       {
9242         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9243           TEST_DrawLevelField(x, y - 1);
9244       }
9245       else
9246       {
9247         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9248           TEST_DrawLevelField(x, y + 1);
9249       }
9250
9251       Feld[x][y] = Store[x][y];
9252       Store[x][y] = 0;
9253       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9254       TEST_DrawLevelField(x, y);
9255     }
9256   }
9257 }
9258
9259 void MauerAbleger(int ax, int ay)
9260 {
9261   int element = Feld[ax][ay];
9262   int graphic = el2img(element);
9263   boolean oben_frei = FALSE, unten_frei = FALSE;
9264   boolean links_frei = FALSE, rechts_frei = FALSE;
9265   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9266   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9267   boolean new_wall = FALSE;
9268
9269   if (IS_ANIMATED(graphic))
9270     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9271
9272   if (!MovDelay[ax][ay])        /* start building new wall */
9273     MovDelay[ax][ay] = 6;
9274
9275   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9276   {
9277     MovDelay[ax][ay]--;
9278     if (MovDelay[ax][ay])
9279       return;
9280   }
9281
9282   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9283     oben_frei = TRUE;
9284   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9285     unten_frei = TRUE;
9286   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9287     links_frei = TRUE;
9288   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9289     rechts_frei = TRUE;
9290
9291   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9292       element == EL_EXPANDABLE_WALL_ANY)
9293   {
9294     if (oben_frei)
9295     {
9296       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9297       Store[ax][ay-1] = element;
9298       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9299       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9300         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9301                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9302       new_wall = TRUE;
9303     }
9304     if (unten_frei)
9305     {
9306       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9307       Store[ax][ay+1] = element;
9308       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9309       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9310         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9311                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9312       new_wall = TRUE;
9313     }
9314   }
9315
9316   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9317       element == EL_EXPANDABLE_WALL_ANY ||
9318       element == EL_EXPANDABLE_WALL ||
9319       element == EL_BD_EXPANDABLE_WALL)
9320   {
9321     if (links_frei)
9322     {
9323       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9324       Store[ax-1][ay] = element;
9325       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9326       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9327         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9328                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9329       new_wall = TRUE;
9330     }
9331
9332     if (rechts_frei)
9333     {
9334       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9335       Store[ax+1][ay] = element;
9336       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9337       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9338         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9339                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9340       new_wall = TRUE;
9341     }
9342   }
9343
9344   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9345     TEST_DrawLevelField(ax, ay);
9346
9347   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9348     oben_massiv = TRUE;
9349   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9350     unten_massiv = TRUE;
9351   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9352     links_massiv = TRUE;
9353   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9354     rechts_massiv = TRUE;
9355
9356   if (((oben_massiv && unten_massiv) ||
9357        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9358        element == EL_EXPANDABLE_WALL) &&
9359       ((links_massiv && rechts_massiv) ||
9360        element == EL_EXPANDABLE_WALL_VERTICAL))
9361     Feld[ax][ay] = EL_WALL;
9362
9363   if (new_wall)
9364     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9365 }
9366
9367 void MauerAblegerStahl(int ax, int ay)
9368 {
9369   int element = Feld[ax][ay];
9370   int graphic = el2img(element);
9371   boolean oben_frei = FALSE, unten_frei = FALSE;
9372   boolean links_frei = FALSE, rechts_frei = FALSE;
9373   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9374   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9375   boolean new_wall = FALSE;
9376
9377   if (IS_ANIMATED(graphic))
9378     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9379
9380   if (!MovDelay[ax][ay])        /* start building new wall */
9381     MovDelay[ax][ay] = 6;
9382
9383   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9384   {
9385     MovDelay[ax][ay]--;
9386     if (MovDelay[ax][ay])
9387       return;
9388   }
9389
9390   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9391     oben_frei = TRUE;
9392   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9393     unten_frei = TRUE;
9394   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9395     links_frei = TRUE;
9396   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9397     rechts_frei = TRUE;
9398
9399   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9400       element == EL_EXPANDABLE_STEELWALL_ANY)
9401   {
9402     if (oben_frei)
9403     {
9404       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9405       Store[ax][ay-1] = element;
9406       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9407       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9408         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9409                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9410       new_wall = TRUE;
9411     }
9412     if (unten_frei)
9413     {
9414       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9415       Store[ax][ay+1] = element;
9416       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9417       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9418         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9419                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9420       new_wall = TRUE;
9421     }
9422   }
9423
9424   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9425       element == EL_EXPANDABLE_STEELWALL_ANY)
9426   {
9427     if (links_frei)
9428     {
9429       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9430       Store[ax-1][ay] = element;
9431       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9432       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9433         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9434                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9435       new_wall = TRUE;
9436     }
9437
9438     if (rechts_frei)
9439     {
9440       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9441       Store[ax+1][ay] = element;
9442       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9443       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9444         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9445                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9446       new_wall = TRUE;
9447     }
9448   }
9449
9450   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9451     oben_massiv = TRUE;
9452   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9453     unten_massiv = TRUE;
9454   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9455     links_massiv = TRUE;
9456   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9457     rechts_massiv = TRUE;
9458
9459   if (((oben_massiv && unten_massiv) ||
9460        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9461       ((links_massiv && rechts_massiv) ||
9462        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9463     Feld[ax][ay] = EL_STEELWALL;
9464
9465   if (new_wall)
9466     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9467 }
9468
9469 void CheckForDragon(int x, int y)
9470 {
9471   int i, j;
9472   boolean dragon_found = FALSE;
9473   static int xy[4][2] =
9474   {
9475     { 0, -1 },
9476     { -1, 0 },
9477     { +1, 0 },
9478     { 0, +1 }
9479   };
9480
9481   for (i = 0; i < NUM_DIRECTIONS; i++)
9482   {
9483     for (j = 0; j < 4; j++)
9484     {
9485       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9486
9487       if (IN_LEV_FIELD(xx, yy) &&
9488           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9489       {
9490         if (Feld[xx][yy] == EL_DRAGON)
9491           dragon_found = TRUE;
9492       }
9493       else
9494         break;
9495     }
9496   }
9497
9498   if (!dragon_found)
9499   {
9500     for (i = 0; i < NUM_DIRECTIONS; i++)
9501     {
9502       for (j = 0; j < 3; j++)
9503       {
9504         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9505   
9506         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9507         {
9508           Feld[xx][yy] = EL_EMPTY;
9509           TEST_DrawLevelField(xx, yy);
9510         }
9511         else
9512           break;
9513       }
9514     }
9515   }
9516 }
9517
9518 static void InitBuggyBase(int x, int y)
9519 {
9520   int element = Feld[x][y];
9521   int activating_delay = FRAMES_PER_SECOND / 4;
9522
9523   ChangeDelay[x][y] =
9524     (element == EL_SP_BUGGY_BASE ?
9525      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9526      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9527      activating_delay :
9528      element == EL_SP_BUGGY_BASE_ACTIVE ?
9529      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9530 }
9531
9532 static void WarnBuggyBase(int x, int y)
9533 {
9534   int i;
9535   static int xy[4][2] =
9536   {
9537     { 0, -1 },
9538     { -1, 0 },
9539     { +1, 0 },
9540     { 0, +1 }
9541   };
9542
9543   for (i = 0; i < NUM_DIRECTIONS; i++)
9544   {
9545     int xx = x + xy[i][0];
9546     int yy = y + xy[i][1];
9547
9548     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9549     {
9550       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9551
9552       break;
9553     }
9554   }
9555 }
9556
9557 static void InitTrap(int x, int y)
9558 {
9559   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9560 }
9561
9562 static void ActivateTrap(int x, int y)
9563 {
9564   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9565 }
9566
9567 static void ChangeActiveTrap(int x, int y)
9568 {
9569   int graphic = IMG_TRAP_ACTIVE;
9570
9571   /* if new animation frame was drawn, correct crumbled sand border */
9572   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9573     TEST_DrawLevelFieldCrumbled(x, y);
9574 }
9575
9576 static int getSpecialActionElement(int element, int number, int base_element)
9577 {
9578   return (element != EL_EMPTY ? element :
9579           number != -1 ? base_element + number - 1 :
9580           EL_EMPTY);
9581 }
9582
9583 static int getModifiedActionNumber(int value_old, int operator, int operand,
9584                                    int value_min, int value_max)
9585 {
9586   int value_new = (operator == CA_MODE_SET      ? operand :
9587                    operator == CA_MODE_ADD      ? value_old + operand :
9588                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9589                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9590                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9591                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9592                    value_old);
9593
9594   return (value_new < value_min ? value_min :
9595           value_new > value_max ? value_max :
9596           value_new);
9597 }
9598
9599 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9600 {
9601   struct ElementInfo *ei = &element_info[element];
9602   struct ElementChangeInfo *change = &ei->change_page[page];
9603   int target_element = change->target_element;
9604   int action_type = change->action_type;
9605   int action_mode = change->action_mode;
9606   int action_arg = change->action_arg;
9607   int action_element = change->action_element;
9608   int i;
9609
9610   if (!change->has_action)
9611     return;
9612
9613   /* ---------- determine action paramater values -------------------------- */
9614
9615   int level_time_value =
9616     (level.time > 0 ? TimeLeft :
9617      TimePlayed);
9618
9619   int action_arg_element_raw =
9620     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9621      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9622      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9623      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9624      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9625      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9626      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9627      EL_EMPTY);
9628   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9629
9630   int action_arg_direction =
9631     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9632      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9633      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9634      change->actual_trigger_side :
9635      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9636      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9637      MV_NONE);
9638
9639   int action_arg_number_min =
9640     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9641      CA_ARG_MIN);
9642
9643   int action_arg_number_max =
9644     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9645      action_type == CA_SET_LEVEL_GEMS ? 999 :
9646      action_type == CA_SET_LEVEL_TIME ? 9999 :
9647      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9648      action_type == CA_SET_CE_VALUE ? 9999 :
9649      action_type == CA_SET_CE_SCORE ? 9999 :
9650      CA_ARG_MAX);
9651
9652   int action_arg_number_reset =
9653     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9654      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9655      action_type == CA_SET_LEVEL_TIME ? level.time :
9656      action_type == CA_SET_LEVEL_SCORE ? 0 :
9657      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9658      action_type == CA_SET_CE_SCORE ? 0 :
9659      0);
9660
9661   int action_arg_number =
9662     (action_arg <= CA_ARG_MAX ? action_arg :
9663      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9664      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9665      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9666      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9667      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9668      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9669      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9670      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9671      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9672      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9673      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9674      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9675      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9676      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9677      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9678      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9679      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9680      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9681      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9682      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9683      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9684      -1);
9685
9686   int action_arg_number_old =
9687     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9688      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9689      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9690      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9691      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9692      0);
9693
9694   int action_arg_number_new =
9695     getModifiedActionNumber(action_arg_number_old,
9696                             action_mode, action_arg_number,
9697                             action_arg_number_min, action_arg_number_max);
9698
9699   int trigger_player_bits =
9700     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9701      change->actual_trigger_player_bits : change->trigger_player);
9702
9703   int action_arg_player_bits =
9704     (action_arg >= CA_ARG_PLAYER_1 &&
9705      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9706      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9707      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9708      PLAYER_BITS_ANY);
9709
9710   /* ---------- execute action  -------------------------------------------- */
9711
9712   switch (action_type)
9713   {
9714     case CA_NO_ACTION:
9715     {
9716       return;
9717     }
9718
9719     /* ---------- level actions  ------------------------------------------- */
9720
9721     case CA_RESTART_LEVEL:
9722     {
9723       game.restart_level = TRUE;
9724
9725       break;
9726     }
9727
9728     case CA_SHOW_ENVELOPE:
9729     {
9730       int element = getSpecialActionElement(action_arg_element,
9731                                             action_arg_number, EL_ENVELOPE_1);
9732
9733       if (IS_ENVELOPE(element))
9734         local_player->show_envelope = element;
9735
9736       break;
9737     }
9738
9739     case CA_SET_LEVEL_TIME:
9740     {
9741       if (level.time > 0)       /* only modify limited time value */
9742       {
9743         TimeLeft = action_arg_number_new;
9744
9745         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9746
9747         DisplayGameControlValues();
9748
9749         if (!TimeLeft && setup.time_limit)
9750           for (i = 0; i < MAX_PLAYERS; i++)
9751             KillPlayer(&stored_player[i]);
9752       }
9753
9754       break;
9755     }
9756
9757     case CA_SET_LEVEL_SCORE:
9758     {
9759       local_player->score = action_arg_number_new;
9760
9761       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9762
9763       DisplayGameControlValues();
9764
9765       break;
9766     }
9767
9768     case CA_SET_LEVEL_GEMS:
9769     {
9770       local_player->gems_still_needed = action_arg_number_new;
9771
9772       game.snapshot.collected_item = TRUE;
9773
9774       game_panel_controls[GAME_PANEL_GEMS].value =
9775         local_player->gems_still_needed;
9776
9777       DisplayGameControlValues();
9778
9779       break;
9780     }
9781
9782     case CA_SET_LEVEL_WIND:
9783     {
9784       game.wind_direction = action_arg_direction;
9785
9786       break;
9787     }
9788
9789     case CA_SET_LEVEL_RANDOM_SEED:
9790     {
9791       /* ensure that setting a new random seed while playing is predictable */
9792       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9793
9794       break;
9795     }
9796
9797     /* ---------- player actions  ------------------------------------------ */
9798
9799     case CA_MOVE_PLAYER:
9800     {
9801       /* automatically move to the next field in specified direction */
9802       for (i = 0; i < MAX_PLAYERS; i++)
9803         if (trigger_player_bits & (1 << i))
9804           stored_player[i].programmed_action = action_arg_direction;
9805
9806       break;
9807     }
9808
9809     case CA_EXIT_PLAYER:
9810     {
9811       for (i = 0; i < MAX_PLAYERS; i++)
9812         if (action_arg_player_bits & (1 << i))
9813           PlayerWins(&stored_player[i]);
9814
9815       break;
9816     }
9817
9818     case CA_KILL_PLAYER:
9819     {
9820       for (i = 0; i < MAX_PLAYERS; i++)
9821         if (action_arg_player_bits & (1 << i))
9822           KillPlayer(&stored_player[i]);
9823
9824       break;
9825     }
9826
9827     case CA_SET_PLAYER_KEYS:
9828     {
9829       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9830       int element = getSpecialActionElement(action_arg_element,
9831                                             action_arg_number, EL_KEY_1);
9832
9833       if (IS_KEY(element))
9834       {
9835         for (i = 0; i < MAX_PLAYERS; i++)
9836         {
9837           if (trigger_player_bits & (1 << i))
9838           {
9839             stored_player[i].key[KEY_NR(element)] = key_state;
9840
9841             DrawGameDoorValues();
9842           }
9843         }
9844       }
9845
9846       break;
9847     }
9848
9849     case CA_SET_PLAYER_SPEED:
9850     {
9851       for (i = 0; i < MAX_PLAYERS; i++)
9852       {
9853         if (trigger_player_bits & (1 << i))
9854         {
9855           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9856
9857           if (action_arg == CA_ARG_SPEED_FASTER &&
9858               stored_player[i].cannot_move)
9859           {
9860             action_arg_number = STEPSIZE_VERY_SLOW;
9861           }
9862           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9863                    action_arg == CA_ARG_SPEED_FASTER)
9864           {
9865             action_arg_number = 2;
9866             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9867                            CA_MODE_MULTIPLY);
9868           }
9869           else if (action_arg == CA_ARG_NUMBER_RESET)
9870           {
9871             action_arg_number = level.initial_player_stepsize[i];
9872           }
9873
9874           move_stepsize =
9875             getModifiedActionNumber(move_stepsize,
9876                                     action_mode,
9877                                     action_arg_number,
9878                                     action_arg_number_min,
9879                                     action_arg_number_max);
9880
9881           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9882         }
9883       }
9884
9885       break;
9886     }
9887
9888     case CA_SET_PLAYER_SHIELD:
9889     {
9890       for (i = 0; i < MAX_PLAYERS; i++)
9891       {
9892         if (trigger_player_bits & (1 << i))
9893         {
9894           if (action_arg == CA_ARG_SHIELD_OFF)
9895           {
9896             stored_player[i].shield_normal_time_left = 0;
9897             stored_player[i].shield_deadly_time_left = 0;
9898           }
9899           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9900           {
9901             stored_player[i].shield_normal_time_left = 999999;
9902           }
9903           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9904           {
9905             stored_player[i].shield_normal_time_left = 999999;
9906             stored_player[i].shield_deadly_time_left = 999999;
9907           }
9908         }
9909       }
9910
9911       break;
9912     }
9913
9914     case CA_SET_PLAYER_GRAVITY:
9915     {
9916       for (i = 0; i < MAX_PLAYERS; i++)
9917       {
9918         if (trigger_player_bits & (1 << i))
9919         {
9920           stored_player[i].gravity =
9921             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9922              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9923              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9924              stored_player[i].gravity);
9925         }
9926       }
9927
9928       break;
9929     }
9930
9931     case CA_SET_PLAYER_ARTWORK:
9932     {
9933       for (i = 0; i < MAX_PLAYERS; i++)
9934       {
9935         if (trigger_player_bits & (1 << i))
9936         {
9937           int artwork_element = action_arg_element;
9938
9939           if (action_arg == CA_ARG_ELEMENT_RESET)
9940             artwork_element =
9941               (level.use_artwork_element[i] ? level.artwork_element[i] :
9942                stored_player[i].element_nr);
9943
9944           if (stored_player[i].artwork_element != artwork_element)
9945             stored_player[i].Frame = 0;
9946
9947           stored_player[i].artwork_element = artwork_element;
9948
9949           SetPlayerWaiting(&stored_player[i], FALSE);
9950
9951           /* set number of special actions for bored and sleeping animation */
9952           stored_player[i].num_special_action_bored =
9953             get_num_special_action(artwork_element,
9954                                    ACTION_BORING_1, ACTION_BORING_LAST);
9955           stored_player[i].num_special_action_sleeping =
9956             get_num_special_action(artwork_element,
9957                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9958         }
9959       }
9960
9961       break;
9962     }
9963
9964     case CA_SET_PLAYER_INVENTORY:
9965     {
9966       for (i = 0; i < MAX_PLAYERS; i++)
9967       {
9968         struct PlayerInfo *player = &stored_player[i];
9969         int j, k;
9970
9971         if (trigger_player_bits & (1 << i))
9972         {
9973           int inventory_element = action_arg_element;
9974
9975           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9976               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9977               action_arg == CA_ARG_ELEMENT_ACTION)
9978           {
9979             int element = inventory_element;
9980             int collect_count = element_info[element].collect_count_initial;
9981
9982             if (!IS_CUSTOM_ELEMENT(element))
9983               collect_count = 1;
9984
9985             if (collect_count == 0)
9986               player->inventory_infinite_element = element;
9987             else
9988               for (k = 0; k < collect_count; k++)
9989                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9990                   player->inventory_element[player->inventory_size++] =
9991                     element;
9992           }
9993           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9994                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9995                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9996           {
9997             if (player->inventory_infinite_element != EL_UNDEFINED &&
9998                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9999                                      action_arg_element_raw))
10000               player->inventory_infinite_element = EL_UNDEFINED;
10001
10002             for (k = 0, j = 0; j < player->inventory_size; j++)
10003             {
10004               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10005                                         action_arg_element_raw))
10006                 player->inventory_element[k++] = player->inventory_element[j];
10007             }
10008
10009             player->inventory_size = k;
10010           }
10011           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10012           {
10013             if (player->inventory_size > 0)
10014             {
10015               for (j = 0; j < player->inventory_size - 1; j++)
10016                 player->inventory_element[j] = player->inventory_element[j + 1];
10017
10018               player->inventory_size--;
10019             }
10020           }
10021           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10022           {
10023             if (player->inventory_size > 0)
10024               player->inventory_size--;
10025           }
10026           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10027           {
10028             player->inventory_infinite_element = EL_UNDEFINED;
10029             player->inventory_size = 0;
10030           }
10031           else if (action_arg == CA_ARG_INVENTORY_RESET)
10032           {
10033             player->inventory_infinite_element = EL_UNDEFINED;
10034             player->inventory_size = 0;
10035
10036             if (level.use_initial_inventory[i])
10037             {
10038               for (j = 0; j < level.initial_inventory_size[i]; j++)
10039               {
10040                 int element = level.initial_inventory_content[i][j];
10041                 int collect_count = element_info[element].collect_count_initial;
10042
10043                 if (!IS_CUSTOM_ELEMENT(element))
10044                   collect_count = 1;
10045
10046                 if (collect_count == 0)
10047                   player->inventory_infinite_element = element;
10048                 else
10049                   for (k = 0; k < collect_count; k++)
10050                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10051                       player->inventory_element[player->inventory_size++] =
10052                         element;
10053               }
10054             }
10055           }
10056         }
10057       }
10058
10059       break;
10060     }
10061
10062     /* ---------- CE actions  ---------------------------------------------- */
10063
10064     case CA_SET_CE_VALUE:
10065     {
10066       int last_ce_value = CustomValue[x][y];
10067
10068       CustomValue[x][y] = action_arg_number_new;
10069
10070       if (CustomValue[x][y] != last_ce_value)
10071       {
10072         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10073         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10074
10075         if (CustomValue[x][y] == 0)
10076         {
10077           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10078           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10079         }
10080       }
10081
10082       break;
10083     }
10084
10085     case CA_SET_CE_SCORE:
10086     {
10087       int last_ce_score = ei->collect_score;
10088
10089       ei->collect_score = action_arg_number_new;
10090
10091       if (ei->collect_score != last_ce_score)
10092       {
10093         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10094         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10095
10096         if (ei->collect_score == 0)
10097         {
10098           int xx, yy;
10099
10100           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10101           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10102
10103           /*
10104             This is a very special case that seems to be a mixture between
10105             CheckElementChange() and CheckTriggeredElementChange(): while
10106             the first one only affects single elements that are triggered
10107             directly, the second one affects multiple elements in the playfield
10108             that are triggered indirectly by another element. This is a third
10109             case: Changing the CE score always affects multiple identical CEs,
10110             so every affected CE must be checked, not only the single CE for
10111             which the CE score was changed in the first place (as every instance
10112             of that CE shares the same CE score, and therefore also can change)!
10113           */
10114           SCAN_PLAYFIELD(xx, yy)
10115           {
10116             if (Feld[xx][yy] == element)
10117               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10118                                  CE_SCORE_GETS_ZERO);
10119           }
10120         }
10121       }
10122
10123       break;
10124     }
10125
10126     case CA_SET_CE_ARTWORK:
10127     {
10128       int artwork_element = action_arg_element;
10129       boolean reset_frame = FALSE;
10130       int xx, yy;
10131
10132       if (action_arg == CA_ARG_ELEMENT_RESET)
10133         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10134                            element);
10135
10136       if (ei->gfx_element != artwork_element)
10137         reset_frame = TRUE;
10138
10139       ei->gfx_element = artwork_element;
10140
10141       SCAN_PLAYFIELD(xx, yy)
10142       {
10143         if (Feld[xx][yy] == element)
10144         {
10145           if (reset_frame)
10146           {
10147             ResetGfxAnimation(xx, yy);
10148             ResetRandomAnimationValue(xx, yy);
10149           }
10150
10151           TEST_DrawLevelField(xx, yy);
10152         }
10153       }
10154
10155       break;
10156     }
10157
10158     /* ---------- engine actions  ------------------------------------------ */
10159
10160     case CA_SET_ENGINE_SCAN_MODE:
10161     {
10162       InitPlayfieldScanMode(action_arg);
10163
10164       break;
10165     }
10166
10167     default:
10168       break;
10169   }
10170 }
10171
10172 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10173 {
10174   int old_element = Feld[x][y];
10175   int new_element = GetElementFromGroupElement(element);
10176   int previous_move_direction = MovDir[x][y];
10177   int last_ce_value = CustomValue[x][y];
10178   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10179   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10180   boolean add_player_onto_element = (new_element_is_player &&
10181                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10182                                      IS_WALKABLE(old_element));
10183
10184   if (!add_player_onto_element)
10185   {
10186     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10187       RemoveMovingField(x, y);
10188     else
10189       RemoveField(x, y);
10190
10191     Feld[x][y] = new_element;
10192
10193     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10194       MovDir[x][y] = previous_move_direction;
10195
10196     if (element_info[new_element].use_last_ce_value)
10197       CustomValue[x][y] = last_ce_value;
10198
10199     InitField_WithBug1(x, y, FALSE);
10200
10201     new_element = Feld[x][y];   /* element may have changed */
10202
10203     ResetGfxAnimation(x, y);
10204     ResetRandomAnimationValue(x, y);
10205
10206     TEST_DrawLevelField(x, y);
10207
10208     if (GFX_CRUMBLED(new_element))
10209       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10210   }
10211
10212   /* check if element under the player changes from accessible to unaccessible
10213      (needed for special case of dropping element which then changes) */
10214   /* (must be checked after creating new element for walkable group elements) */
10215   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10216       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10217   {
10218     Bang(x, y);
10219
10220     return;
10221   }
10222
10223   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10224   if (new_element_is_player)
10225     RelocatePlayer(x, y, new_element);
10226
10227   if (is_change)
10228     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10229
10230   TestIfBadThingTouchesPlayer(x, y);
10231   TestIfPlayerTouchesCustomElement(x, y);
10232   TestIfElementTouchesCustomElement(x, y);
10233 }
10234
10235 static void CreateField(int x, int y, int element)
10236 {
10237   CreateFieldExt(x, y, element, FALSE);
10238 }
10239
10240 static void CreateElementFromChange(int x, int y, int element)
10241 {
10242   element = GET_VALID_RUNTIME_ELEMENT(element);
10243
10244   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10245   {
10246     int old_element = Feld[x][y];
10247
10248     /* prevent changed element from moving in same engine frame
10249        unless both old and new element can either fall or move */
10250     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10251         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10252       Stop[x][y] = TRUE;
10253   }
10254
10255   CreateFieldExt(x, y, element, TRUE);
10256 }
10257
10258 static boolean ChangeElement(int x, int y, int element, int page)
10259 {
10260   struct ElementInfo *ei = &element_info[element];
10261   struct ElementChangeInfo *change = &ei->change_page[page];
10262   int ce_value = CustomValue[x][y];
10263   int ce_score = ei->collect_score;
10264   int target_element;
10265   int old_element = Feld[x][y];
10266
10267   /* always use default change event to prevent running into a loop */
10268   if (ChangeEvent[x][y] == -1)
10269     ChangeEvent[x][y] = CE_DELAY;
10270
10271   if (ChangeEvent[x][y] == CE_DELAY)
10272   {
10273     /* reset actual trigger element, trigger player and action element */
10274     change->actual_trigger_element = EL_EMPTY;
10275     change->actual_trigger_player = EL_EMPTY;
10276     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10277     change->actual_trigger_side = CH_SIDE_NONE;
10278     change->actual_trigger_ce_value = 0;
10279     change->actual_trigger_ce_score = 0;
10280   }
10281
10282   /* do not change elements more than a specified maximum number of changes */
10283   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10284     return FALSE;
10285
10286   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10287
10288   if (change->explode)
10289   {
10290     Bang(x, y);
10291
10292     return TRUE;
10293   }
10294
10295   if (change->use_target_content)
10296   {
10297     boolean complete_replace = TRUE;
10298     boolean can_replace[3][3];
10299     int xx, yy;
10300
10301     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10302     {
10303       boolean is_empty;
10304       boolean is_walkable;
10305       boolean is_diggable;
10306       boolean is_collectible;
10307       boolean is_removable;
10308       boolean is_destructible;
10309       int ex = x + xx - 1;
10310       int ey = y + yy - 1;
10311       int content_element = change->target_content.e[xx][yy];
10312       int e;
10313
10314       can_replace[xx][yy] = TRUE;
10315
10316       if (ex == x && ey == y)   /* do not check changing element itself */
10317         continue;
10318
10319       if (content_element == EL_EMPTY_SPACE)
10320       {
10321         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10322
10323         continue;
10324       }
10325
10326       if (!IN_LEV_FIELD(ex, ey))
10327       {
10328         can_replace[xx][yy] = FALSE;
10329         complete_replace = FALSE;
10330
10331         continue;
10332       }
10333
10334       e = Feld[ex][ey];
10335
10336       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10337         e = MovingOrBlocked2Element(ex, ey);
10338
10339       is_empty = (IS_FREE(ex, ey) ||
10340                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10341
10342       is_walkable     = (is_empty || IS_WALKABLE(e));
10343       is_diggable     = (is_empty || IS_DIGGABLE(e));
10344       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10345       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10346       is_removable    = (is_diggable || is_collectible);
10347
10348       can_replace[xx][yy] =
10349         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10350           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10351           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10352           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10353           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10354           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10355          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10356
10357       if (!can_replace[xx][yy])
10358         complete_replace = FALSE;
10359     }
10360
10361     if (!change->only_if_complete || complete_replace)
10362     {
10363       boolean something_has_changed = FALSE;
10364
10365       if (change->only_if_complete && change->use_random_replace &&
10366           RND(100) < change->random_percentage)
10367         return FALSE;
10368
10369       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10370       {
10371         int ex = x + xx - 1;
10372         int ey = y + yy - 1;
10373         int content_element;
10374
10375         if (can_replace[xx][yy] && (!change->use_random_replace ||
10376                                     RND(100) < change->random_percentage))
10377         {
10378           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10379             RemoveMovingField(ex, ey);
10380
10381           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10382
10383           content_element = change->target_content.e[xx][yy];
10384           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10385                                               ce_value, ce_score);
10386
10387           CreateElementFromChange(ex, ey, target_element);
10388
10389           something_has_changed = TRUE;
10390
10391           /* for symmetry reasons, freeze newly created border elements */
10392           if (ex != x || ey != y)
10393             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10394         }
10395       }
10396
10397       if (something_has_changed)
10398       {
10399         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10400         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10401       }
10402     }
10403   }
10404   else
10405   {
10406     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10407                                         ce_value, ce_score);
10408
10409     if (element == EL_DIAGONAL_GROWING ||
10410         element == EL_DIAGONAL_SHRINKING)
10411     {
10412       target_element = Store[x][y];
10413
10414       Store[x][y] = EL_EMPTY;
10415     }
10416
10417     CreateElementFromChange(x, y, target_element);
10418
10419     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10420     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10421   }
10422
10423   /* this uses direct change before indirect change */
10424   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10425
10426   return TRUE;
10427 }
10428
10429 static void HandleElementChange(int x, int y, int page)
10430 {
10431   int element = MovingOrBlocked2Element(x, y);
10432   struct ElementInfo *ei = &element_info[element];
10433   struct ElementChangeInfo *change = &ei->change_page[page];
10434   boolean handle_action_before_change = FALSE;
10435
10436 #ifdef DEBUG
10437   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10438       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10439   {
10440     printf("\n\n");
10441     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10442            x, y, element, element_info[element].token_name);
10443     printf("HandleElementChange(): This should never happen!\n");
10444     printf("\n\n");
10445   }
10446 #endif
10447
10448   /* this can happen with classic bombs on walkable, changing elements */
10449   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10450   {
10451     return;
10452   }
10453
10454   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10455   {
10456     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10457
10458     if (change->can_change)
10459     {
10460       /* !!! not clear why graphic animation should be reset at all here !!! */
10461       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10462       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10463
10464       /*
10465         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10466
10467         When using an animation frame delay of 1 (this only happens with
10468         "sp_zonk.moving.left/right" in the classic graphics), the default
10469         (non-moving) animation shows wrong animation frames (while the
10470         moving animation, like "sp_zonk.moving.left/right", is correct,
10471         so this graphical bug never shows up with the classic graphics).
10472         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10473         be drawn instead of the correct frames 0,1,2,3. This is caused by
10474         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10475         an element change: First when the change delay ("ChangeDelay[][]")
10476         counter has reached zero after decrementing, then a second time in
10477         the next frame (after "GfxFrame[][]" was already incremented) when
10478         "ChangeDelay[][]" is reset to the initial delay value again.
10479
10480         This causes frame 0 to be drawn twice, while the last frame won't
10481         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10482
10483         As some animations may already be cleverly designed around this bug
10484         (at least the "Snake Bite" snake tail animation does this), it cannot
10485         simply be fixed here without breaking such existing animations.
10486         Unfortunately, it cannot easily be detected if a graphics set was
10487         designed "before" or "after" the bug was fixed. As a workaround,
10488         a new graphics set option "game.graphics_engine_version" was added
10489         to be able to specify the game's major release version for which the
10490         graphics set was designed, which can then be used to decide if the
10491         bugfix should be used (version 4 and above) or not (version 3 or
10492         below, or if no version was specified at all, as with old sets).
10493
10494         (The wrong/fixed animation frames can be tested with the test level set
10495         "test_gfxframe" and level "000", which contains a specially prepared
10496         custom element at level position (x/y) == (11/9) which uses the zonk
10497         animation mentioned above. Using "game.graphics_engine_version: 4"
10498         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10499         This can also be seen from the debug output for this test element.)
10500       */
10501
10502       /* when a custom element is about to change (for example by change delay),
10503          do not reset graphic animation when the custom element is moving */
10504       if (game.graphics_engine_version < 4 &&
10505           !IS_MOVING(x, y))
10506       {
10507         ResetGfxAnimation(x, y);
10508         ResetRandomAnimationValue(x, y);
10509       }
10510
10511       if (change->pre_change_function)
10512         change->pre_change_function(x, y);
10513     }
10514   }
10515
10516   ChangeDelay[x][y]--;
10517
10518   if (ChangeDelay[x][y] != 0)           /* continue element change */
10519   {
10520     if (change->can_change)
10521     {
10522       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10523
10524       if (IS_ANIMATED(graphic))
10525         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10526
10527       if (change->change_function)
10528         change->change_function(x, y);
10529     }
10530   }
10531   else                                  /* finish element change */
10532   {
10533     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10534     {
10535       page = ChangePage[x][y];
10536       ChangePage[x][y] = -1;
10537
10538       change = &ei->change_page[page];
10539     }
10540
10541     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10542     {
10543       ChangeDelay[x][y] = 1;            /* try change after next move step */
10544       ChangePage[x][y] = page;          /* remember page to use for change */
10545
10546       return;
10547     }
10548
10549     /* special case: set new level random seed before changing element */
10550     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10551       handle_action_before_change = TRUE;
10552
10553     if (change->has_action && handle_action_before_change)
10554       ExecuteCustomElementAction(x, y, element, page);
10555
10556     if (change->can_change)
10557     {
10558       if (ChangeElement(x, y, element, page))
10559       {
10560         if (change->post_change_function)
10561           change->post_change_function(x, y);
10562       }
10563     }
10564
10565     if (change->has_action && !handle_action_before_change)
10566       ExecuteCustomElementAction(x, y, element, page);
10567   }
10568 }
10569
10570 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10571                                               int trigger_element,
10572                                               int trigger_event,
10573                                               int trigger_player,
10574                                               int trigger_side,
10575                                               int trigger_page)
10576 {
10577   boolean change_done_any = FALSE;
10578   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10579   int i;
10580
10581   if (!(trigger_events[trigger_element][trigger_event]))
10582     return FALSE;
10583
10584   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10585
10586   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10587   {
10588     int element = EL_CUSTOM_START + i;
10589     boolean change_done = FALSE;
10590     int p;
10591
10592     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10593         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10594       continue;
10595
10596     for (p = 0; p < element_info[element].num_change_pages; p++)
10597     {
10598       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10599
10600       if (change->can_change_or_has_action &&
10601           change->has_event[trigger_event] &&
10602           change->trigger_side & trigger_side &&
10603           change->trigger_player & trigger_player &&
10604           change->trigger_page & trigger_page_bits &&
10605           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10606       {
10607         change->actual_trigger_element = trigger_element;
10608         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10609         change->actual_trigger_player_bits = trigger_player;
10610         change->actual_trigger_side = trigger_side;
10611         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10612         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10613
10614         if ((change->can_change && !change_done) || change->has_action)
10615         {
10616           int x, y;
10617
10618           SCAN_PLAYFIELD(x, y)
10619           {
10620             if (Feld[x][y] == element)
10621             {
10622               if (change->can_change && !change_done)
10623               {
10624                 /* if element already changed in this frame, not only prevent
10625                    another element change (checked in ChangeElement()), but
10626                    also prevent additional element actions for this element */
10627
10628                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10629                     !level.use_action_after_change_bug)
10630                   continue;
10631
10632                 ChangeDelay[x][y] = 1;
10633                 ChangeEvent[x][y] = trigger_event;
10634
10635                 HandleElementChange(x, y, p);
10636               }
10637               else if (change->has_action)
10638               {
10639                 /* if element already changed in this frame, not only prevent
10640                    another element change (checked in ChangeElement()), but
10641                    also prevent additional element actions for this element */
10642
10643                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10644                     !level.use_action_after_change_bug)
10645                   continue;
10646
10647                 ExecuteCustomElementAction(x, y, element, p);
10648                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10649               }
10650             }
10651           }
10652
10653           if (change->can_change)
10654           {
10655             change_done = TRUE;
10656             change_done_any = TRUE;
10657           }
10658         }
10659       }
10660     }
10661   }
10662
10663   RECURSION_LOOP_DETECTION_END();
10664
10665   return change_done_any;
10666 }
10667
10668 static boolean CheckElementChangeExt(int x, int y,
10669                                      int element,
10670                                      int trigger_element,
10671                                      int trigger_event,
10672                                      int trigger_player,
10673                                      int trigger_side)
10674 {
10675   boolean change_done = FALSE;
10676   int p;
10677
10678   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10679       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10680     return FALSE;
10681
10682   if (Feld[x][y] == EL_BLOCKED)
10683   {
10684     Blocked2Moving(x, y, &x, &y);
10685     element = Feld[x][y];
10686   }
10687
10688   /* check if element has already changed or is about to change after moving */
10689   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10690        Feld[x][y] != element) ||
10691
10692       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10693        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10694         ChangePage[x][y] != -1)))
10695     return FALSE;
10696
10697   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10698
10699   for (p = 0; p < element_info[element].num_change_pages; p++)
10700   {
10701     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10702
10703     /* check trigger element for all events where the element that is checked
10704        for changing interacts with a directly adjacent element -- this is
10705        different to element changes that affect other elements to change on the
10706        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10707     boolean check_trigger_element =
10708       (trigger_event == CE_TOUCHING_X ||
10709        trigger_event == CE_HITTING_X ||
10710        trigger_event == CE_HIT_BY_X ||
10711        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10712
10713     if (change->can_change_or_has_action &&
10714         change->has_event[trigger_event] &&
10715         change->trigger_side & trigger_side &&
10716         change->trigger_player & trigger_player &&
10717         (!check_trigger_element ||
10718          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10719     {
10720       change->actual_trigger_element = trigger_element;
10721       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10722       change->actual_trigger_player_bits = trigger_player;
10723       change->actual_trigger_side = trigger_side;
10724       change->actual_trigger_ce_value = CustomValue[x][y];
10725       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10726
10727       /* special case: trigger element not at (x,y) position for some events */
10728       if (check_trigger_element)
10729       {
10730         static struct
10731         {
10732           int dx, dy;
10733         } move_xy[] =
10734           {
10735             {  0,  0 },
10736             { -1,  0 },
10737             { +1,  0 },
10738             {  0,  0 },
10739             {  0, -1 },
10740             {  0,  0 }, { 0, 0 }, { 0, 0 },
10741             {  0, +1 }
10742           };
10743
10744         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10745         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10746
10747         change->actual_trigger_ce_value = CustomValue[xx][yy];
10748         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10749       }
10750
10751       if (change->can_change && !change_done)
10752       {
10753         ChangeDelay[x][y] = 1;
10754         ChangeEvent[x][y] = trigger_event;
10755
10756         HandleElementChange(x, y, p);
10757
10758         change_done = TRUE;
10759       }
10760       else if (change->has_action)
10761       {
10762         ExecuteCustomElementAction(x, y, element, p);
10763         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10764       }
10765     }
10766   }
10767
10768   RECURSION_LOOP_DETECTION_END();
10769
10770   return change_done;
10771 }
10772
10773 static void PlayPlayerSound(struct PlayerInfo *player)
10774 {
10775   int jx = player->jx, jy = player->jy;
10776   int sound_element = player->artwork_element;
10777   int last_action = player->last_action_waiting;
10778   int action = player->action_waiting;
10779
10780   if (player->is_waiting)
10781   {
10782     if (action != last_action)
10783       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10784     else
10785       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10786   }
10787   else
10788   {
10789     if (action != last_action)
10790       StopSound(element_info[sound_element].sound[last_action]);
10791
10792     if (last_action == ACTION_SLEEPING)
10793       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10794   }
10795 }
10796
10797 static void PlayAllPlayersSound()
10798 {
10799   int i;
10800
10801   for (i = 0; i < MAX_PLAYERS; i++)
10802     if (stored_player[i].active)
10803       PlayPlayerSound(&stored_player[i]);
10804 }
10805
10806 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10807 {
10808   boolean last_waiting = player->is_waiting;
10809   int move_dir = player->MovDir;
10810
10811   player->dir_waiting = move_dir;
10812   player->last_action_waiting = player->action_waiting;
10813
10814   if (is_waiting)
10815   {
10816     if (!last_waiting)          /* not waiting -> waiting */
10817     {
10818       player->is_waiting = TRUE;
10819
10820       player->frame_counter_bored =
10821         FrameCounter +
10822         game.player_boring_delay_fixed +
10823         GetSimpleRandom(game.player_boring_delay_random);
10824       player->frame_counter_sleeping =
10825         FrameCounter +
10826         game.player_sleeping_delay_fixed +
10827         GetSimpleRandom(game.player_sleeping_delay_random);
10828
10829       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10830     }
10831
10832     if (game.player_sleeping_delay_fixed +
10833         game.player_sleeping_delay_random > 0 &&
10834         player->anim_delay_counter == 0 &&
10835         player->post_delay_counter == 0 &&
10836         FrameCounter >= player->frame_counter_sleeping)
10837       player->is_sleeping = TRUE;
10838     else if (game.player_boring_delay_fixed +
10839              game.player_boring_delay_random > 0 &&
10840              FrameCounter >= player->frame_counter_bored)
10841       player->is_bored = TRUE;
10842
10843     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10844                               player->is_bored ? ACTION_BORING :
10845                               ACTION_WAITING);
10846
10847     if (player->is_sleeping && player->use_murphy)
10848     {
10849       /* special case for sleeping Murphy when leaning against non-free tile */
10850
10851       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10852           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10853            !IS_MOVING(player->jx - 1, player->jy)))
10854         move_dir = MV_LEFT;
10855       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10856                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10857                 !IS_MOVING(player->jx + 1, player->jy)))
10858         move_dir = MV_RIGHT;
10859       else
10860         player->is_sleeping = FALSE;
10861
10862       player->dir_waiting = move_dir;
10863     }
10864
10865     if (player->is_sleeping)
10866     {
10867       if (player->num_special_action_sleeping > 0)
10868       {
10869         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10870         {
10871           int last_special_action = player->special_action_sleeping;
10872           int num_special_action = player->num_special_action_sleeping;
10873           int special_action =
10874             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10875              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10876              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10877              last_special_action + 1 : ACTION_SLEEPING);
10878           int special_graphic =
10879             el_act_dir2img(player->artwork_element, special_action, move_dir);
10880
10881           player->anim_delay_counter =
10882             graphic_info[special_graphic].anim_delay_fixed +
10883             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10884           player->post_delay_counter =
10885             graphic_info[special_graphic].post_delay_fixed +
10886             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10887
10888           player->special_action_sleeping = special_action;
10889         }
10890
10891         if (player->anim_delay_counter > 0)
10892         {
10893           player->action_waiting = player->special_action_sleeping;
10894           player->anim_delay_counter--;
10895         }
10896         else if (player->post_delay_counter > 0)
10897         {
10898           player->post_delay_counter--;
10899         }
10900       }
10901     }
10902     else if (player->is_bored)
10903     {
10904       if (player->num_special_action_bored > 0)
10905       {
10906         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10907         {
10908           int special_action =
10909             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10910           int special_graphic =
10911             el_act_dir2img(player->artwork_element, special_action, move_dir);
10912
10913           player->anim_delay_counter =
10914             graphic_info[special_graphic].anim_delay_fixed +
10915             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10916           player->post_delay_counter =
10917             graphic_info[special_graphic].post_delay_fixed +
10918             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10919
10920           player->special_action_bored = special_action;
10921         }
10922
10923         if (player->anim_delay_counter > 0)
10924         {
10925           player->action_waiting = player->special_action_bored;
10926           player->anim_delay_counter--;
10927         }
10928         else if (player->post_delay_counter > 0)
10929         {
10930           player->post_delay_counter--;
10931         }
10932       }
10933     }
10934   }
10935   else if (last_waiting)        /* waiting -> not waiting */
10936   {
10937     player->is_waiting = FALSE;
10938     player->is_bored = FALSE;
10939     player->is_sleeping = FALSE;
10940
10941     player->frame_counter_bored = -1;
10942     player->frame_counter_sleeping = -1;
10943
10944     player->anim_delay_counter = 0;
10945     player->post_delay_counter = 0;
10946
10947     player->dir_waiting = player->MovDir;
10948     player->action_waiting = ACTION_DEFAULT;
10949
10950     player->special_action_bored = ACTION_DEFAULT;
10951     player->special_action_sleeping = ACTION_DEFAULT;
10952   }
10953 }
10954
10955 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10956 {
10957   if ((!player->is_moving  && player->was_moving) ||
10958       (player->MovPos == 0 && player->was_moving) ||
10959       (player->is_snapping && !player->was_snapping) ||
10960       (player->is_dropping && !player->was_dropping))
10961   {
10962     if (!CheckSaveEngineSnapshotToList())
10963       return;
10964
10965     player->was_moving = FALSE;
10966     player->was_snapping = TRUE;
10967     player->was_dropping = TRUE;
10968   }
10969   else
10970   {
10971     if (player->is_moving)
10972       player->was_moving = TRUE;
10973
10974     if (!player->is_snapping)
10975       player->was_snapping = FALSE;
10976
10977     if (!player->is_dropping)
10978       player->was_dropping = FALSE;
10979   }
10980 }
10981
10982 static void CheckSingleStepMode(struct PlayerInfo *player)
10983 {
10984   if (tape.single_step && tape.recording && !tape.pausing)
10985   {
10986     /* as it is called "single step mode", just return to pause mode when the
10987        player stopped moving after one tile (or never starts moving at all) */
10988     if (!player->is_moving &&
10989         !player->is_pushing &&
10990         !player->is_dropping_pressed)
10991     {
10992       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10993       SnapField(player, 0, 0);                  /* stop snapping */
10994     }
10995   }
10996
10997   CheckSaveEngineSnapshot(player);
10998 }
10999
11000 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11001 {
11002   int left      = player_action & JOY_LEFT;
11003   int right     = player_action & JOY_RIGHT;
11004   int up        = player_action & JOY_UP;
11005   int down      = player_action & JOY_DOWN;
11006   int button1   = player_action & JOY_BUTTON_1;
11007   int button2   = player_action & JOY_BUTTON_2;
11008   int dx        = (left ? -1 : right ? 1 : 0);
11009   int dy        = (up   ? -1 : down  ? 1 : 0);
11010
11011   if (!player->active || tape.pausing)
11012     return 0;
11013
11014   if (player_action)
11015   {
11016     if (button1)
11017       SnapField(player, dx, dy);
11018     else
11019     {
11020       if (button2)
11021         DropElement(player);
11022
11023       MovePlayer(player, dx, dy);
11024     }
11025
11026     CheckSingleStepMode(player);
11027
11028     SetPlayerWaiting(player, FALSE);
11029
11030     return player_action;
11031   }
11032   else
11033   {
11034     /* no actions for this player (no input at player's configured device) */
11035
11036     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11037     SnapField(player, 0, 0);
11038     CheckGravityMovementWhenNotMoving(player);
11039
11040     if (player->MovPos == 0)
11041       SetPlayerWaiting(player, TRUE);
11042
11043     if (player->MovPos == 0)    /* needed for tape.playing */
11044       player->is_moving = FALSE;
11045
11046     player->is_dropping = FALSE;
11047     player->is_dropping_pressed = FALSE;
11048     player->drop_pressed_delay = 0;
11049
11050     CheckSingleStepMode(player);
11051
11052     return 0;
11053   }
11054 }
11055
11056 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11057                                          byte *tape_action)
11058 {
11059   if (!tape.use_mouse)
11060     return;
11061
11062   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11063   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11064   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11065 }
11066
11067 static void SetTapeActionFromMouseAction(byte *tape_action,
11068                                          struct MouseActionInfo *mouse_action)
11069 {
11070   if (!tape.use_mouse)
11071     return;
11072
11073   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11074   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11075   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11076 }
11077
11078 static void CheckLevelTime()
11079 {
11080   int i;
11081
11082   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11083   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11084   {
11085     if (level.native_em_level->lev->home == 0)  /* all players at home */
11086     {
11087       PlayerWins(local_player);
11088
11089       AllPlayersGone = TRUE;
11090
11091       level.native_em_level->lev->home = -1;
11092     }
11093
11094     if (level.native_em_level->ply[0]->alive == 0 &&
11095         level.native_em_level->ply[1]->alive == 0 &&
11096         level.native_em_level->ply[2]->alive == 0 &&
11097         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11098       AllPlayersGone = TRUE;
11099   }
11100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11101   {
11102     if (game_sp.LevelSolved &&
11103         !game_sp.GameOver)                              /* game won */
11104     {
11105       PlayerWins(local_player);
11106
11107       game_sp.GameOver = TRUE;
11108
11109       AllPlayersGone = TRUE;
11110     }
11111
11112     if (game_sp.GameOver)                               /* game lost */
11113       AllPlayersGone = TRUE;
11114   }
11115   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11116   {
11117     if (game_mm.level_solved &&
11118         !game_mm.game_over)                             /* game won */
11119     {
11120       PlayerWins(local_player);
11121
11122       game_mm.game_over = TRUE;
11123
11124       AllPlayersGone = TRUE;
11125     }
11126
11127     if (game_mm.game_over)                              /* game lost */
11128       AllPlayersGone = TRUE;
11129   }
11130
11131   if (TimeFrames >= FRAMES_PER_SECOND)
11132   {
11133     TimeFrames = 0;
11134     TapeTime++;
11135
11136     for (i = 0; i < MAX_PLAYERS; i++)
11137     {
11138       struct PlayerInfo *player = &stored_player[i];
11139
11140       if (SHIELD_ON(player))
11141       {
11142         player->shield_normal_time_left--;
11143
11144         if (player->shield_deadly_time_left > 0)
11145           player->shield_deadly_time_left--;
11146       }
11147     }
11148
11149     if (!local_player->LevelSolved && !level.use_step_counter)
11150     {
11151       TimePlayed++;
11152
11153       if (TimeLeft > 0)
11154       {
11155         TimeLeft--;
11156
11157         if (TimeLeft <= 10 && setup.time_limit)
11158           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11159
11160         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11161            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11162
11163         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11164
11165         if (!TimeLeft && setup.time_limit)
11166         {
11167           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11168             level.native_em_level->lev->killed_out_of_time = TRUE;
11169           else
11170             for (i = 0; i < MAX_PLAYERS; i++)
11171               KillPlayer(&stored_player[i]);
11172         }
11173       }
11174       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11175       {
11176         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11177       }
11178
11179       level.native_em_level->lev->time =
11180         (game.no_time_limit ? TimePlayed : TimeLeft);
11181     }
11182
11183     if (tape.recording || tape.playing)
11184       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11185   }
11186
11187   if (tape.recording || tape.playing)
11188     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11189
11190   UpdateAndDisplayGameControlValues();
11191 }
11192
11193 void AdvanceFrameAndPlayerCounters(int player_nr)
11194 {
11195   int i;
11196
11197   /* advance frame counters (global frame counter and time frame counter) */
11198   FrameCounter++;
11199   TimeFrames++;
11200
11201   /* advance player counters (counters for move delay, move animation etc.) */
11202   for (i = 0; i < MAX_PLAYERS; i++)
11203   {
11204     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11205     int move_delay_value = stored_player[i].move_delay_value;
11206     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11207
11208     if (!advance_player_counters)       /* not all players may be affected */
11209       continue;
11210
11211     if (move_frames == 0)       /* less than one move per game frame */
11212     {
11213       int stepsize = TILEX / move_delay_value;
11214       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11215       int count = (stored_player[i].is_moving ?
11216                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11217
11218       if (count % delay == 0)
11219         move_frames = 1;
11220     }
11221
11222     stored_player[i].Frame += move_frames;
11223
11224     if (stored_player[i].MovPos != 0)
11225       stored_player[i].StepFrame += move_frames;
11226
11227     if (stored_player[i].move_delay > 0)
11228       stored_player[i].move_delay--;
11229
11230     /* due to bugs in previous versions, counter must count up, not down */
11231     if (stored_player[i].push_delay != -1)
11232       stored_player[i].push_delay++;
11233
11234     if (stored_player[i].drop_delay > 0)
11235       stored_player[i].drop_delay--;
11236
11237     if (stored_player[i].is_dropping_pressed)
11238       stored_player[i].drop_pressed_delay++;
11239   }
11240 }
11241
11242 void StartGameActions(boolean init_network_game, boolean record_tape,
11243                       int random_seed)
11244 {
11245   unsigned int new_random_seed = InitRND(random_seed);
11246
11247   if (record_tape)
11248     TapeStartRecording(new_random_seed);
11249
11250 #if defined(NETWORK_AVALIABLE)
11251   if (init_network_game)
11252   {
11253     SendToServer_StartPlaying();
11254
11255     return;
11256   }
11257 #endif
11258
11259   InitGame();
11260 }
11261
11262 void GameActionsExt()
11263 {
11264 #if 0
11265   static unsigned int game_frame_delay = 0;
11266 #endif
11267   unsigned int game_frame_delay_value;
11268   byte *recorded_player_action;
11269   byte summarized_player_action = 0;
11270   byte tape_action[MAX_PLAYERS];
11271   int i;
11272
11273   /* detect endless loops, caused by custom element programming */
11274   if (recursion_loop_detected && recursion_loop_depth == 0)
11275   {
11276     char *message = getStringCat3("Internal Error! Element ",
11277                                   EL_NAME(recursion_loop_element),
11278                                   " caused endless loop! Quit the game?");
11279
11280     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11281           EL_NAME(recursion_loop_element));
11282
11283     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11284
11285     recursion_loop_detected = FALSE;    /* if game should be continued */
11286
11287     free(message);
11288
11289     return;
11290   }
11291
11292   if (game.restart_level)
11293     StartGameActions(options.network, setup.autorecord, level.random_seed);
11294
11295   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11296   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11297   {
11298     if (level.native_em_level->lev->home == 0)  /* all players at home */
11299     {
11300       PlayerWins(local_player);
11301
11302       AllPlayersGone = TRUE;
11303
11304       level.native_em_level->lev->home = -1;
11305     }
11306
11307     if (level.native_em_level->ply[0]->alive == 0 &&
11308         level.native_em_level->ply[1]->alive == 0 &&
11309         level.native_em_level->ply[2]->alive == 0 &&
11310         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11311       AllPlayersGone = TRUE;
11312   }
11313   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11314   {
11315     if (game_sp.LevelSolved &&
11316         !game_sp.GameOver)                              /* game won */
11317     {
11318       PlayerWins(local_player);
11319
11320       game_sp.GameOver = TRUE;
11321
11322       AllPlayersGone = TRUE;
11323     }
11324
11325     if (game_sp.GameOver)                               /* game lost */
11326       AllPlayersGone = TRUE;
11327   }
11328   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11329   {
11330     if (game_mm.level_solved &&
11331         !game_mm.game_over)                             /* game won */
11332     {
11333       PlayerWins(local_player);
11334
11335       game_mm.game_over = TRUE;
11336
11337       AllPlayersGone = TRUE;
11338     }
11339
11340     if (game_mm.game_over)                              /* game lost */
11341       AllPlayersGone = TRUE;
11342   }
11343
11344   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11345     GameWon();
11346
11347   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11348     TapeStop();
11349
11350   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11351     return;
11352
11353   game_frame_delay_value =
11354     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11355
11356   if (tape.playing && tape.warp_forward && !tape.pausing)
11357     game_frame_delay_value = 0;
11358
11359   SetVideoFrameDelay(game_frame_delay_value);
11360
11361 #if 0
11362 #if 0
11363   /* ---------- main game synchronization point ---------- */
11364
11365   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11366
11367   printf("::: skip == %d\n", skip);
11368
11369 #else
11370   /* ---------- main game synchronization point ---------- */
11371
11372   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11373 #endif
11374 #endif
11375
11376   if (network_playing && !network_player_action_received)
11377   {
11378     /* try to get network player actions in time */
11379
11380 #if defined(NETWORK_AVALIABLE)
11381     /* last chance to get network player actions without main loop delay */
11382     HandleNetworking();
11383 #endif
11384
11385     /* game was quit by network peer */
11386     if (game_status != GAME_MODE_PLAYING)
11387       return;
11388
11389     if (!network_player_action_received)
11390       return;           /* failed to get network player actions in time */
11391
11392     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11393   }
11394
11395   if (tape.pausing)
11396     return;
11397
11398   /* at this point we know that we really continue executing the game */
11399
11400   network_player_action_received = FALSE;
11401
11402   /* when playing tape, read previously recorded player input from tape data */
11403   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11404
11405   local_player->effective_mouse_action = local_player->mouse_action;
11406
11407   if (recorded_player_action != NULL)
11408     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11409                                  recorded_player_action);
11410
11411   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11412   if (tape.pausing)
11413     return;
11414
11415   if (tape.set_centered_player)
11416   {
11417     game.centered_player_nr_next = tape.centered_player_nr_next;
11418     game.set_centered_player = TRUE;
11419   }
11420
11421   for (i = 0; i < MAX_PLAYERS; i++)
11422   {
11423     summarized_player_action |= stored_player[i].action;
11424
11425     if (!network_playing && (game.team_mode || tape.playing))
11426       stored_player[i].effective_action = stored_player[i].action;
11427   }
11428
11429 #if defined(NETWORK_AVALIABLE)
11430   if (network_playing)
11431     SendToServer_MovePlayer(summarized_player_action);
11432 #endif
11433
11434   // summarize all actions at local players mapped input device position
11435   // (this allows using different input devices in single player mode)
11436   if (!options.network && !game.team_mode)
11437     stored_player[map_player_action[local_player->index_nr]].effective_action =
11438       summarized_player_action;
11439
11440   if (tape.recording &&
11441       setup.team_mode &&
11442       setup.input_on_focus &&
11443       game.centered_player_nr != -1)
11444   {
11445     for (i = 0; i < MAX_PLAYERS; i++)
11446       stored_player[i].effective_action =
11447         (i == game.centered_player_nr ? summarized_player_action : 0);
11448   }
11449
11450   if (recorded_player_action != NULL)
11451     for (i = 0; i < MAX_PLAYERS; i++)
11452       stored_player[i].effective_action = recorded_player_action[i];
11453
11454   for (i = 0; i < MAX_PLAYERS; i++)
11455   {
11456     tape_action[i] = stored_player[i].effective_action;
11457
11458     /* (this may happen in the RND game engine if a player was not present on
11459        the playfield on level start, but appeared later from a custom element */
11460     if (setup.team_mode &&
11461         tape.recording &&
11462         tape_action[i] &&
11463         !tape.player_participates[i])
11464       tape.player_participates[i] = TRUE;
11465   }
11466
11467   SetTapeActionFromMouseAction(tape_action,
11468                                &local_player->effective_mouse_action);
11469
11470   /* only record actions from input devices, but not programmed actions */
11471   if (tape.recording)
11472     TapeRecordAction(tape_action);
11473
11474 #if USE_NEW_PLAYER_ASSIGNMENTS
11475   // !!! also map player actions in single player mode !!!
11476   // if (game.team_mode)
11477   if (1)
11478   {
11479     byte mapped_action[MAX_PLAYERS];
11480
11481 #if DEBUG_PLAYER_ACTIONS
11482     printf(":::");
11483     for (i = 0; i < MAX_PLAYERS; i++)
11484       printf(" %d, ", stored_player[i].effective_action);
11485 #endif
11486
11487     for (i = 0; i < MAX_PLAYERS; i++)
11488       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11489
11490     for (i = 0; i < MAX_PLAYERS; i++)
11491       stored_player[i].effective_action = mapped_action[i];
11492
11493 #if DEBUG_PLAYER_ACTIONS
11494     printf(" =>");
11495     for (i = 0; i < MAX_PLAYERS; i++)
11496       printf(" %d, ", stored_player[i].effective_action);
11497     printf("\n");
11498 #endif
11499   }
11500 #if DEBUG_PLAYER_ACTIONS
11501   else
11502   {
11503     printf(":::");
11504     for (i = 0; i < MAX_PLAYERS; i++)
11505       printf(" %d, ", stored_player[i].effective_action);
11506     printf("\n");
11507   }
11508 #endif
11509 #endif
11510
11511   for (i = 0; i < MAX_PLAYERS; i++)
11512   {
11513     // allow engine snapshot in case of changed movement attempt
11514     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11515         (stored_player[i].effective_action & KEY_MOTION))
11516       game.snapshot.changed_action = TRUE;
11517
11518     // allow engine snapshot in case of snapping/dropping attempt
11519     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11520         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11521       game.snapshot.changed_action = TRUE;
11522
11523     game.snapshot.last_action[i] = stored_player[i].effective_action;
11524   }
11525
11526   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11527   {
11528     GameActions_EM_Main();
11529   }
11530   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11531   {
11532     GameActions_SP_Main();
11533   }
11534   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11535   {
11536     GameActions_MM_Main();
11537   }
11538   else
11539   {
11540     GameActions_RND_Main();
11541   }
11542
11543   BlitScreenToBitmap(backbuffer);
11544
11545   CheckLevelTime();
11546
11547   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11548
11549   if (global.show_frames_per_second)
11550   {
11551     static unsigned int fps_counter = 0;
11552     static int fps_frames = 0;
11553     unsigned int fps_delay_ms = Counter() - fps_counter;
11554
11555     fps_frames++;
11556
11557     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11558     {
11559       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11560
11561       fps_frames = 0;
11562       fps_counter = Counter();
11563
11564       /* always draw FPS to screen after FPS value was updated */
11565       redraw_mask |= REDRAW_FPS;
11566     }
11567
11568     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11569     if (GetDrawDeactivationMask() == REDRAW_NONE)
11570       redraw_mask |= REDRAW_FPS;
11571   }
11572 }
11573
11574 static void GameActions_CheckSaveEngineSnapshot()
11575 {
11576   if (!game.snapshot.save_snapshot)
11577     return;
11578
11579   // clear flag for saving snapshot _before_ saving snapshot
11580   game.snapshot.save_snapshot = FALSE;
11581
11582   SaveEngineSnapshotToList();
11583 }
11584
11585 void GameActions()
11586 {
11587   GameActionsExt();
11588
11589   GameActions_CheckSaveEngineSnapshot();
11590 }
11591
11592 void GameActions_EM_Main()
11593 {
11594   byte effective_action[MAX_PLAYERS];
11595   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11596   int i;
11597
11598   for (i = 0; i < MAX_PLAYERS; i++)
11599     effective_action[i] = stored_player[i].effective_action;
11600
11601   GameActions_EM(effective_action, warp_mode);
11602 }
11603
11604 void GameActions_SP_Main()
11605 {
11606   byte effective_action[MAX_PLAYERS];
11607   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11608   int i;
11609
11610   for (i = 0; i < MAX_PLAYERS; i++)
11611     effective_action[i] = stored_player[i].effective_action;
11612
11613   GameActions_SP(effective_action, warp_mode);
11614
11615   for (i = 0; i < MAX_PLAYERS; i++)
11616   {
11617     if (stored_player[i].force_dropping)
11618       stored_player[i].action |= KEY_BUTTON_DROP;
11619
11620     stored_player[i].force_dropping = FALSE;
11621   }
11622 }
11623
11624 void GameActions_MM_Main()
11625 {
11626   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11627
11628   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11629 }
11630
11631 void GameActions_RND_Main()
11632 {
11633   GameActions_RND();
11634 }
11635
11636 void GameActions_RND()
11637 {
11638   int magic_wall_x = 0, magic_wall_y = 0;
11639   int i, x, y, element, graphic, last_gfx_frame;
11640
11641   InitPlayfieldScanModeVars();
11642
11643   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11644   {
11645     SCAN_PLAYFIELD(x, y)
11646     {
11647       ChangeCount[x][y] = 0;
11648       ChangeEvent[x][y] = -1;
11649     }
11650   }
11651
11652   if (game.set_centered_player)
11653   {
11654     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11655
11656     /* switching to "all players" only possible if all players fit to screen */
11657     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11658     {
11659       game.centered_player_nr_next = game.centered_player_nr;
11660       game.set_centered_player = FALSE;
11661     }
11662
11663     /* do not switch focus to non-existing (or non-active) player */
11664     if (game.centered_player_nr_next >= 0 &&
11665         !stored_player[game.centered_player_nr_next].active)
11666     {
11667       game.centered_player_nr_next = game.centered_player_nr;
11668       game.set_centered_player = FALSE;
11669     }
11670   }
11671
11672   if (game.set_centered_player &&
11673       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11674   {
11675     int sx, sy;
11676
11677     if (game.centered_player_nr_next == -1)
11678     {
11679       setScreenCenteredToAllPlayers(&sx, &sy);
11680     }
11681     else
11682     {
11683       sx = stored_player[game.centered_player_nr_next].jx;
11684       sy = stored_player[game.centered_player_nr_next].jy;
11685     }
11686
11687     game.centered_player_nr = game.centered_player_nr_next;
11688     game.set_centered_player = FALSE;
11689
11690     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11691     DrawGameDoorValues();
11692   }
11693
11694   for (i = 0; i < MAX_PLAYERS; i++)
11695   {
11696     int actual_player_action = stored_player[i].effective_action;
11697
11698 #if 1
11699     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11700        - rnd_equinox_tetrachloride 048
11701        - rnd_equinox_tetrachloride_ii 096
11702        - rnd_emanuel_schmieg 002
11703        - doctor_sloan_ww 001, 020
11704     */
11705     if (stored_player[i].MovPos == 0)
11706       CheckGravityMovement(&stored_player[i]);
11707 #endif
11708
11709     /* overwrite programmed action with tape action */
11710     if (stored_player[i].programmed_action)
11711       actual_player_action = stored_player[i].programmed_action;
11712
11713     PlayerActions(&stored_player[i], actual_player_action);
11714
11715     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11716   }
11717
11718   ScrollScreen(NULL, SCROLL_GO_ON);
11719
11720   /* for backwards compatibility, the following code emulates a fixed bug that
11721      occured when pushing elements (causing elements that just made their last
11722      pushing step to already (if possible) make their first falling step in the
11723      same game frame, which is bad); this code is also needed to use the famous
11724      "spring push bug" which is used in older levels and might be wanted to be
11725      used also in newer levels, but in this case the buggy pushing code is only
11726      affecting the "spring" element and no other elements */
11727
11728   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11729   {
11730     for (i = 0; i < MAX_PLAYERS; i++)
11731     {
11732       struct PlayerInfo *player = &stored_player[i];
11733       int x = player->jx;
11734       int y = player->jy;
11735
11736       if (player->active && player->is_pushing && player->is_moving &&
11737           IS_MOVING(x, y) &&
11738           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11739            Feld[x][y] == EL_SPRING))
11740       {
11741         ContinueMoving(x, y);
11742
11743         /* continue moving after pushing (this is actually a bug) */
11744         if (!IS_MOVING(x, y))
11745           Stop[x][y] = FALSE;
11746       }
11747     }
11748   }
11749
11750   SCAN_PLAYFIELD(x, y)
11751   {
11752     ChangeCount[x][y] = 0;
11753     ChangeEvent[x][y] = -1;
11754
11755     /* this must be handled before main playfield loop */
11756     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11757     {
11758       MovDelay[x][y]--;
11759       if (MovDelay[x][y] <= 0)
11760         RemoveField(x, y);
11761     }
11762
11763     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11764     {
11765       MovDelay[x][y]--;
11766       if (MovDelay[x][y] <= 0)
11767       {
11768         RemoveField(x, y);
11769         TEST_DrawLevelField(x, y);
11770
11771         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11772       }
11773     }
11774
11775 #if DEBUG
11776     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11777     {
11778       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11779       printf("GameActions(): This should never happen!\n");
11780
11781       ChangePage[x][y] = -1;
11782     }
11783 #endif
11784
11785     Stop[x][y] = FALSE;
11786     if (WasJustMoving[x][y] > 0)
11787       WasJustMoving[x][y]--;
11788     if (WasJustFalling[x][y] > 0)
11789       WasJustFalling[x][y]--;
11790     if (CheckCollision[x][y] > 0)
11791       CheckCollision[x][y]--;
11792     if (CheckImpact[x][y] > 0)
11793       CheckImpact[x][y]--;
11794
11795     GfxFrame[x][y]++;
11796
11797     /* reset finished pushing action (not done in ContinueMoving() to allow
11798        continuous pushing animation for elements with zero push delay) */
11799     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11800     {
11801       ResetGfxAnimation(x, y);
11802       TEST_DrawLevelField(x, y);
11803     }
11804
11805 #if DEBUG
11806     if (IS_BLOCKED(x, y))
11807     {
11808       int oldx, oldy;
11809
11810       Blocked2Moving(x, y, &oldx, &oldy);
11811       if (!IS_MOVING(oldx, oldy))
11812       {
11813         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11814         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11815         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11816         printf("GameActions(): This should never happen!\n");
11817       }
11818     }
11819 #endif
11820   }
11821
11822   SCAN_PLAYFIELD(x, y)
11823   {
11824     element = Feld[x][y];
11825     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11826     last_gfx_frame = GfxFrame[x][y];
11827
11828     ResetGfxFrame(x, y);
11829
11830     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11831       DrawLevelGraphicAnimation(x, y, graphic);
11832
11833     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11834         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11835       ResetRandomAnimationValue(x, y);
11836
11837     SetRandomAnimationValue(x, y);
11838
11839     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11840
11841     if (IS_INACTIVE(element))
11842     {
11843       if (IS_ANIMATED(graphic))
11844         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11845
11846       continue;
11847     }
11848
11849     /* this may take place after moving, so 'element' may have changed */
11850     if (IS_CHANGING(x, y) &&
11851         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11852     {
11853       int page = element_info[element].event_page_nr[CE_DELAY];
11854
11855       HandleElementChange(x, y, page);
11856
11857       element = Feld[x][y];
11858       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11859     }
11860
11861     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11862     {
11863       StartMoving(x, y);
11864
11865       element = Feld[x][y];
11866       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11867
11868       if (IS_ANIMATED(graphic) &&
11869           !IS_MOVING(x, y) &&
11870           !Stop[x][y])
11871         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11872
11873       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11874         TEST_DrawTwinkleOnField(x, y);
11875     }
11876     else if (element == EL_ACID)
11877     {
11878       if (!Stop[x][y])
11879         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11880     }
11881     else if ((element == EL_EXIT_OPEN ||
11882               element == EL_EM_EXIT_OPEN ||
11883               element == EL_SP_EXIT_OPEN ||
11884               element == EL_STEEL_EXIT_OPEN ||
11885               element == EL_EM_STEEL_EXIT_OPEN ||
11886               element == EL_SP_TERMINAL ||
11887               element == EL_SP_TERMINAL_ACTIVE ||
11888               element == EL_EXTRA_TIME ||
11889               element == EL_SHIELD_NORMAL ||
11890               element == EL_SHIELD_DEADLY) &&
11891              IS_ANIMATED(graphic))
11892       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11893     else if (IS_MOVING(x, y))
11894       ContinueMoving(x, y);
11895     else if (IS_ACTIVE_BOMB(element))
11896       CheckDynamite(x, y);
11897     else if (element == EL_AMOEBA_GROWING)
11898       AmoebeWaechst(x, y);
11899     else if (element == EL_AMOEBA_SHRINKING)
11900       AmoebaDisappearing(x, y);
11901
11902 #if !USE_NEW_AMOEBA_CODE
11903     else if (IS_AMOEBALIVE(element))
11904       AmoebeAbleger(x, y);
11905 #endif
11906
11907     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11908       Life(x, y);
11909     else if (element == EL_EXIT_CLOSED)
11910       CheckExit(x, y);
11911     else if (element == EL_EM_EXIT_CLOSED)
11912       CheckExitEM(x, y);
11913     else if (element == EL_STEEL_EXIT_CLOSED)
11914       CheckExitSteel(x, y);
11915     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11916       CheckExitSteelEM(x, y);
11917     else if (element == EL_SP_EXIT_CLOSED)
11918       CheckExitSP(x, y);
11919     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11920              element == EL_EXPANDABLE_STEELWALL_GROWING)
11921       MauerWaechst(x, y);
11922     else if (element == EL_EXPANDABLE_WALL ||
11923              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11924              element == EL_EXPANDABLE_WALL_VERTICAL ||
11925              element == EL_EXPANDABLE_WALL_ANY ||
11926              element == EL_BD_EXPANDABLE_WALL)
11927       MauerAbleger(x, y);
11928     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11929              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11930              element == EL_EXPANDABLE_STEELWALL_ANY)
11931       MauerAblegerStahl(x, y);
11932     else if (element == EL_FLAMES)
11933       CheckForDragon(x, y);
11934     else if (element == EL_EXPLOSION)
11935       ; /* drawing of correct explosion animation is handled separately */
11936     else if (element == EL_ELEMENT_SNAPPING ||
11937              element == EL_DIAGONAL_SHRINKING ||
11938              element == EL_DIAGONAL_GROWING)
11939     {
11940       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11941
11942       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11943     }
11944     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11946
11947     if (IS_BELT_ACTIVE(element))
11948       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11949
11950     if (game.magic_wall_active)
11951     {
11952       int jx = local_player->jx, jy = local_player->jy;
11953
11954       /* play the element sound at the position nearest to the player */
11955       if ((element == EL_MAGIC_WALL_FULL ||
11956            element == EL_MAGIC_WALL_ACTIVE ||
11957            element == EL_MAGIC_WALL_EMPTYING ||
11958            element == EL_BD_MAGIC_WALL_FULL ||
11959            element == EL_BD_MAGIC_WALL_ACTIVE ||
11960            element == EL_BD_MAGIC_WALL_EMPTYING ||
11961            element == EL_DC_MAGIC_WALL_FULL ||
11962            element == EL_DC_MAGIC_WALL_ACTIVE ||
11963            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11964           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11965       {
11966         magic_wall_x = x;
11967         magic_wall_y = y;
11968       }
11969     }
11970   }
11971
11972 #if USE_NEW_AMOEBA_CODE
11973   /* new experimental amoeba growth stuff */
11974   if (!(FrameCounter % 8))
11975   {
11976     static unsigned int random = 1684108901;
11977
11978     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11979     {
11980       x = RND(lev_fieldx);
11981       y = RND(lev_fieldy);
11982       element = Feld[x][y];
11983
11984       if (!IS_PLAYER(x,y) &&
11985           (element == EL_EMPTY ||
11986            CAN_GROW_INTO(element) ||
11987            element == EL_QUICKSAND_EMPTY ||
11988            element == EL_QUICKSAND_FAST_EMPTY ||
11989            element == EL_ACID_SPLASH_LEFT ||
11990            element == EL_ACID_SPLASH_RIGHT))
11991       {
11992         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11993             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11994             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11995             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11996           Feld[x][y] = EL_AMOEBA_DROP;
11997       }
11998
11999       random = random * 129 + 1;
12000     }
12001   }
12002 #endif
12003
12004   game.explosions_delayed = FALSE;
12005
12006   SCAN_PLAYFIELD(x, y)
12007   {
12008     element = Feld[x][y];
12009
12010     if (ExplodeField[x][y])
12011       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12012     else if (element == EL_EXPLOSION)
12013       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12014
12015     ExplodeField[x][y] = EX_TYPE_NONE;
12016   }
12017
12018   game.explosions_delayed = TRUE;
12019
12020   if (game.magic_wall_active)
12021   {
12022     if (!(game.magic_wall_time_left % 4))
12023     {
12024       int element = Feld[magic_wall_x][magic_wall_y];
12025
12026       if (element == EL_BD_MAGIC_WALL_FULL ||
12027           element == EL_BD_MAGIC_WALL_ACTIVE ||
12028           element == EL_BD_MAGIC_WALL_EMPTYING)
12029         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12030       else if (element == EL_DC_MAGIC_WALL_FULL ||
12031                element == EL_DC_MAGIC_WALL_ACTIVE ||
12032                element == EL_DC_MAGIC_WALL_EMPTYING)
12033         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12034       else
12035         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12036     }
12037
12038     if (game.magic_wall_time_left > 0)
12039     {
12040       game.magic_wall_time_left--;
12041
12042       if (!game.magic_wall_time_left)
12043       {
12044         SCAN_PLAYFIELD(x, y)
12045         {
12046           element = Feld[x][y];
12047
12048           if (element == EL_MAGIC_WALL_ACTIVE ||
12049               element == EL_MAGIC_WALL_FULL)
12050           {
12051             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12052             TEST_DrawLevelField(x, y);
12053           }
12054           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12055                    element == EL_BD_MAGIC_WALL_FULL)
12056           {
12057             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12058             TEST_DrawLevelField(x, y);
12059           }
12060           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12061                    element == EL_DC_MAGIC_WALL_FULL)
12062           {
12063             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12064             TEST_DrawLevelField(x, y);
12065           }
12066         }
12067
12068         game.magic_wall_active = FALSE;
12069       }
12070     }
12071   }
12072
12073   if (game.light_time_left > 0)
12074   {
12075     game.light_time_left--;
12076
12077     if (game.light_time_left == 0)
12078       RedrawAllLightSwitchesAndInvisibleElements();
12079   }
12080
12081   if (game.timegate_time_left > 0)
12082   {
12083     game.timegate_time_left--;
12084
12085     if (game.timegate_time_left == 0)
12086       CloseAllOpenTimegates();
12087   }
12088
12089   if (game.lenses_time_left > 0)
12090   {
12091     game.lenses_time_left--;
12092
12093     if (game.lenses_time_left == 0)
12094       RedrawAllInvisibleElementsForLenses();
12095   }
12096
12097   if (game.magnify_time_left > 0)
12098   {
12099     game.magnify_time_left--;
12100
12101     if (game.magnify_time_left == 0)
12102       RedrawAllInvisibleElementsForMagnifier();
12103   }
12104
12105   for (i = 0; i < MAX_PLAYERS; i++)
12106   {
12107     struct PlayerInfo *player = &stored_player[i];
12108
12109     if (SHIELD_ON(player))
12110     {
12111       if (player->shield_deadly_time_left)
12112         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12113       else if (player->shield_normal_time_left)
12114         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12115     }
12116   }
12117
12118 #if USE_DELAYED_GFX_REDRAW
12119   SCAN_PLAYFIELD(x, y)
12120   {
12121     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12122     {
12123       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12124          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12125
12126       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12127         DrawLevelField(x, y);
12128
12129       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12130         DrawLevelFieldCrumbled(x, y);
12131
12132       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12133         DrawLevelFieldCrumbledNeighbours(x, y);
12134
12135       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12136         DrawTwinkleOnField(x, y);
12137     }
12138
12139     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12140   }
12141 #endif
12142
12143   DrawAllPlayers();
12144   PlayAllPlayersSound();
12145
12146   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12147   {
12148     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12149
12150     local_player->show_envelope = 0;
12151   }
12152
12153   /* use random number generator in every frame to make it less predictable */
12154   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12155     RND(1);
12156 }
12157
12158 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12159 {
12160   int min_x = x, min_y = y, max_x = x, max_y = y;
12161   int i;
12162
12163   for (i = 0; i < MAX_PLAYERS; i++)
12164   {
12165     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12166
12167     if (!stored_player[i].active || &stored_player[i] == player)
12168       continue;
12169
12170     min_x = MIN(min_x, jx);
12171     min_y = MIN(min_y, jy);
12172     max_x = MAX(max_x, jx);
12173     max_y = MAX(max_y, jy);
12174   }
12175
12176   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12177 }
12178
12179 static boolean AllPlayersInVisibleScreen()
12180 {
12181   int i;
12182
12183   for (i = 0; i < MAX_PLAYERS; i++)
12184   {
12185     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12186
12187     if (!stored_player[i].active)
12188       continue;
12189
12190     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12191       return FALSE;
12192   }
12193
12194   return TRUE;
12195 }
12196
12197 void ScrollLevel(int dx, int dy)
12198 {
12199   int scroll_offset = 2 * TILEX_VAR;
12200   int x, y;
12201
12202   BlitBitmap(drawto_field, drawto_field,
12203              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12204              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12205              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12206              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12207              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12208              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12209
12210   if (dx != 0)
12211   {
12212     x = (dx == 1 ? BX1 : BX2);
12213     for (y = BY1; y <= BY2; y++)
12214       DrawScreenField(x, y);
12215   }
12216
12217   if (dy != 0)
12218   {
12219     y = (dy == 1 ? BY1 : BY2);
12220     for (x = BX1; x <= BX2; x++)
12221       DrawScreenField(x, y);
12222   }
12223
12224   redraw_mask |= REDRAW_FIELD;
12225 }
12226
12227 static boolean canFallDown(struct PlayerInfo *player)
12228 {
12229   int jx = player->jx, jy = player->jy;
12230
12231   return (IN_LEV_FIELD(jx, jy + 1) &&
12232           (IS_FREE(jx, jy + 1) ||
12233            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12234           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12235           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12236 }
12237
12238 static boolean canPassField(int x, int y, int move_dir)
12239 {
12240   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12241   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12242   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12243   int nextx = x + dx;
12244   int nexty = y + dy;
12245   int element = Feld[x][y];
12246
12247   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12248           !CAN_MOVE(element) &&
12249           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12250           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12251           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12252 }
12253
12254 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12255 {
12256   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12257   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12258   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12259   int newx = x + dx;
12260   int newy = y + dy;
12261
12262   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12263           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12264           (IS_DIGGABLE(Feld[newx][newy]) ||
12265            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12266            canPassField(newx, newy, move_dir)));
12267 }
12268
12269 static void CheckGravityMovement(struct PlayerInfo *player)
12270 {
12271   if (player->gravity && !player->programmed_action)
12272   {
12273     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12274     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12275     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12276     int jx = player->jx, jy = player->jy;
12277     boolean player_is_moving_to_valid_field =
12278       (!player_is_snapping &&
12279        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12280         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12281     boolean player_can_fall_down = canFallDown(player);
12282
12283     if (player_can_fall_down &&
12284         !player_is_moving_to_valid_field)
12285       player->programmed_action = MV_DOWN;
12286   }
12287 }
12288
12289 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12290 {
12291   return CheckGravityMovement(player);
12292
12293   if (player->gravity && !player->programmed_action)
12294   {
12295     int jx = player->jx, jy = player->jy;
12296     boolean field_under_player_is_free =
12297       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12298     boolean player_is_standing_on_valid_field =
12299       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12300        (IS_WALKABLE(Feld[jx][jy]) &&
12301         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12302
12303     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12304       player->programmed_action = MV_DOWN;
12305   }
12306 }
12307
12308 /*
12309   MovePlayerOneStep()
12310   -----------------------------------------------------------------------------
12311   dx, dy:               direction (non-diagonal) to try to move the player to
12312   real_dx, real_dy:     direction as read from input device (can be diagonal)
12313 */
12314
12315 boolean MovePlayerOneStep(struct PlayerInfo *player,
12316                           int dx, int dy, int real_dx, int real_dy)
12317 {
12318   int jx = player->jx, jy = player->jy;
12319   int new_jx = jx + dx, new_jy = jy + dy;
12320   int can_move;
12321   boolean player_can_move = !player->cannot_move;
12322
12323   if (!player->active || (!dx && !dy))
12324     return MP_NO_ACTION;
12325
12326   player->MovDir = (dx < 0 ? MV_LEFT :
12327                     dx > 0 ? MV_RIGHT :
12328                     dy < 0 ? MV_UP :
12329                     dy > 0 ? MV_DOWN :  MV_NONE);
12330
12331   if (!IN_LEV_FIELD(new_jx, new_jy))
12332     return MP_NO_ACTION;
12333
12334   if (!player_can_move)
12335   {
12336     if (player->MovPos == 0)
12337     {
12338       player->is_moving = FALSE;
12339       player->is_digging = FALSE;
12340       player->is_collecting = FALSE;
12341       player->is_snapping = FALSE;
12342       player->is_pushing = FALSE;
12343     }
12344   }
12345
12346   if (!options.network && game.centered_player_nr == -1 &&
12347       !AllPlayersInSight(player, new_jx, new_jy))
12348     return MP_NO_ACTION;
12349
12350   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12351   if (can_move != MP_MOVING)
12352     return can_move;
12353
12354   /* check if DigField() has caused relocation of the player */
12355   if (player->jx != jx || player->jy != jy)
12356     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12357
12358   StorePlayer[jx][jy] = 0;
12359   player->last_jx = jx;
12360   player->last_jy = jy;
12361   player->jx = new_jx;
12362   player->jy = new_jy;
12363   StorePlayer[new_jx][new_jy] = player->element_nr;
12364
12365   if (player->move_delay_value_next != -1)
12366   {
12367     player->move_delay_value = player->move_delay_value_next;
12368     player->move_delay_value_next = -1;
12369   }
12370
12371   player->MovPos =
12372     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12373
12374   player->step_counter++;
12375
12376   PlayerVisit[jx][jy] = FrameCounter;
12377
12378   player->is_moving = TRUE;
12379
12380 #if 1
12381   /* should better be called in MovePlayer(), but this breaks some tapes */
12382   ScrollPlayer(player, SCROLL_INIT);
12383 #endif
12384
12385   return MP_MOVING;
12386 }
12387
12388 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12389 {
12390   int jx = player->jx, jy = player->jy;
12391   int old_jx = jx, old_jy = jy;
12392   int moved = MP_NO_ACTION;
12393
12394   if (!player->active)
12395     return FALSE;
12396
12397   if (!dx && !dy)
12398   {
12399     if (player->MovPos == 0)
12400     {
12401       player->is_moving = FALSE;
12402       player->is_digging = FALSE;
12403       player->is_collecting = FALSE;
12404       player->is_snapping = FALSE;
12405       player->is_pushing = FALSE;
12406     }
12407
12408     return FALSE;
12409   }
12410
12411   if (player->move_delay > 0)
12412     return FALSE;
12413
12414   player->move_delay = -1;              /* set to "uninitialized" value */
12415
12416   /* store if player is automatically moved to next field */
12417   player->is_auto_moving = (player->programmed_action != MV_NONE);
12418
12419   /* remove the last programmed player action */
12420   player->programmed_action = 0;
12421
12422   if (player->MovPos)
12423   {
12424     /* should only happen if pre-1.2 tape recordings are played */
12425     /* this is only for backward compatibility */
12426
12427     int original_move_delay_value = player->move_delay_value;
12428
12429 #if DEBUG
12430     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12431            tape.counter);
12432 #endif
12433
12434     /* scroll remaining steps with finest movement resolution */
12435     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12436
12437     while (player->MovPos)
12438     {
12439       ScrollPlayer(player, SCROLL_GO_ON);
12440       ScrollScreen(NULL, SCROLL_GO_ON);
12441
12442       AdvanceFrameAndPlayerCounters(player->index_nr);
12443
12444       DrawAllPlayers();
12445       BackToFront_WithFrameDelay(0);
12446     }
12447
12448     player->move_delay_value = original_move_delay_value;
12449   }
12450
12451   player->is_active = FALSE;
12452
12453   if (player->last_move_dir & MV_HORIZONTAL)
12454   {
12455     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12456       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12457   }
12458   else
12459   {
12460     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12461       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12462   }
12463
12464   if (!moved && !player->is_active)
12465   {
12466     player->is_moving = FALSE;
12467     player->is_digging = FALSE;
12468     player->is_collecting = FALSE;
12469     player->is_snapping = FALSE;
12470     player->is_pushing = FALSE;
12471   }
12472
12473   jx = player->jx;
12474   jy = player->jy;
12475
12476   if (moved & MP_MOVING && !ScreenMovPos &&
12477       (player->index_nr == game.centered_player_nr ||
12478        game.centered_player_nr == -1))
12479   {
12480     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12481     int offset = game.scroll_delay_value;
12482
12483     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12484     {
12485       /* actual player has left the screen -- scroll in that direction */
12486       if (jx != old_jx)         /* player has moved horizontally */
12487         scroll_x += (jx - old_jx);
12488       else                      /* player has moved vertically */
12489         scroll_y += (jy - old_jy);
12490     }
12491     else
12492     {
12493       if (jx != old_jx)         /* player has moved horizontally */
12494       {
12495         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12496             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12497           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12498
12499         /* don't scroll over playfield boundaries */
12500         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12501           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12502
12503         /* don't scroll more than one field at a time */
12504         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12505
12506         /* don't scroll against the player's moving direction */
12507         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12508             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12509           scroll_x = old_scroll_x;
12510       }
12511       else                      /* player has moved vertically */
12512       {
12513         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12514             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12515           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12516
12517         /* don't scroll over playfield boundaries */
12518         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12519           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12520
12521         /* don't scroll more than one field at a time */
12522         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12523
12524         /* don't scroll against the player's moving direction */
12525         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12526             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12527           scroll_y = old_scroll_y;
12528       }
12529     }
12530
12531     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12532     {
12533       if (!options.network && game.centered_player_nr == -1 &&
12534           !AllPlayersInVisibleScreen())
12535       {
12536         scroll_x = old_scroll_x;
12537         scroll_y = old_scroll_y;
12538       }
12539       else
12540       {
12541         ScrollScreen(player, SCROLL_INIT);
12542         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12543       }
12544     }
12545   }
12546
12547   player->StepFrame = 0;
12548
12549   if (moved & MP_MOVING)
12550   {
12551     if (old_jx != jx && old_jy == jy)
12552       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12553     else if (old_jx == jx && old_jy != jy)
12554       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12555
12556     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12557
12558     player->last_move_dir = player->MovDir;
12559     player->is_moving = TRUE;
12560     player->is_snapping = FALSE;
12561     player->is_switching = FALSE;
12562     player->is_dropping = FALSE;
12563     player->is_dropping_pressed = FALSE;
12564     player->drop_pressed_delay = 0;
12565
12566 #if 0
12567     /* should better be called here than above, but this breaks some tapes */
12568     ScrollPlayer(player, SCROLL_INIT);
12569 #endif
12570   }
12571   else
12572   {
12573     CheckGravityMovementWhenNotMoving(player);
12574
12575     player->is_moving = FALSE;
12576
12577     /* at this point, the player is allowed to move, but cannot move right now
12578        (e.g. because of something blocking the way) -- ensure that the player
12579        is also allowed to move in the next frame (in old versions before 3.1.1,
12580        the player was forced to wait again for eight frames before next try) */
12581
12582     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12583       player->move_delay = 0;   /* allow direct movement in the next frame */
12584   }
12585
12586   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12587     player->move_delay = player->move_delay_value;
12588
12589   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12590   {
12591     TestIfPlayerTouchesBadThing(jx, jy);
12592     TestIfPlayerTouchesCustomElement(jx, jy);
12593   }
12594
12595   if (!player->active)
12596     RemovePlayer(player);
12597
12598   return moved;
12599 }
12600
12601 void ScrollPlayer(struct PlayerInfo *player, int mode)
12602 {
12603   int jx = player->jx, jy = player->jy;
12604   int last_jx = player->last_jx, last_jy = player->last_jy;
12605   int move_stepsize = TILEX / player->move_delay_value;
12606
12607   if (!player->active)
12608     return;
12609
12610   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12611     return;
12612
12613   if (mode == SCROLL_INIT)
12614   {
12615     player->actual_frame_counter = FrameCounter;
12616     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12617
12618     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12619         Feld[last_jx][last_jy] == EL_EMPTY)
12620     {
12621       int last_field_block_delay = 0;   /* start with no blocking at all */
12622       int block_delay_adjustment = player->block_delay_adjustment;
12623
12624       /* if player blocks last field, add delay for exactly one move */
12625       if (player->block_last_field)
12626       {
12627         last_field_block_delay += player->move_delay_value;
12628
12629         /* when blocking enabled, prevent moving up despite gravity */
12630         if (player->gravity && player->MovDir == MV_UP)
12631           block_delay_adjustment = -1;
12632       }
12633
12634       /* add block delay adjustment (also possible when not blocking) */
12635       last_field_block_delay += block_delay_adjustment;
12636
12637       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12638       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12639     }
12640
12641     if (player->MovPos != 0)    /* player has not yet reached destination */
12642       return;
12643   }
12644   else if (!FrameReached(&player->actual_frame_counter, 1))
12645     return;
12646
12647   if (player->MovPos != 0)
12648   {
12649     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12650     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12651
12652     /* before DrawPlayer() to draw correct player graphic for this case */
12653     if (player->MovPos == 0)
12654       CheckGravityMovement(player);
12655   }
12656
12657   if (player->MovPos == 0)      /* player reached destination field */
12658   {
12659     if (player->move_delay_reset_counter > 0)
12660     {
12661       player->move_delay_reset_counter--;
12662
12663       if (player->move_delay_reset_counter == 0)
12664       {
12665         /* continue with normal speed after quickly moving through gate */
12666         HALVE_PLAYER_SPEED(player);
12667
12668         /* be able to make the next move without delay */
12669         player->move_delay = 0;
12670       }
12671     }
12672
12673     player->last_jx = jx;
12674     player->last_jy = jy;
12675
12676     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12677         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12678         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12679         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12680         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12681         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12682         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12683         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12684     {
12685       DrawPlayer(player);       /* needed here only to cleanup last field */
12686       RemovePlayer(player);
12687
12688       if (local_player->friends_still_needed == 0 ||
12689           IS_SP_ELEMENT(Feld[jx][jy]))
12690         PlayerWins(player);
12691     }
12692
12693     /* this breaks one level: "machine", level 000 */
12694     {
12695       int move_direction = player->MovDir;
12696       int enter_side = MV_DIR_OPPOSITE(move_direction);
12697       int leave_side = move_direction;
12698       int old_jx = last_jx;
12699       int old_jy = last_jy;
12700       int old_element = Feld[old_jx][old_jy];
12701       int new_element = Feld[jx][jy];
12702
12703       if (IS_CUSTOM_ELEMENT(old_element))
12704         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12705                                    CE_LEFT_BY_PLAYER,
12706                                    player->index_bit, leave_side);
12707
12708       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12709                                           CE_PLAYER_LEAVES_X,
12710                                           player->index_bit, leave_side);
12711
12712       if (IS_CUSTOM_ELEMENT(new_element))
12713         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12714                                    player->index_bit, enter_side);
12715
12716       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12717                                           CE_PLAYER_ENTERS_X,
12718                                           player->index_bit, enter_side);
12719
12720       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12721                                         CE_MOVE_OF_X, move_direction);
12722     }
12723
12724     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12725     {
12726       TestIfPlayerTouchesBadThing(jx, jy);
12727       TestIfPlayerTouchesCustomElement(jx, jy);
12728
12729       /* needed because pushed element has not yet reached its destination,
12730          so it would trigger a change event at its previous field location */
12731       if (!player->is_pushing)
12732         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12733
12734       if (!player->active)
12735         RemovePlayer(player);
12736     }
12737
12738     if (!local_player->LevelSolved && level.use_step_counter)
12739     {
12740       int i;
12741
12742       TimePlayed++;
12743
12744       if (TimeLeft > 0)
12745       {
12746         TimeLeft--;
12747
12748         if (TimeLeft <= 10 && setup.time_limit)
12749           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12750
12751         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12752
12753         DisplayGameControlValues();
12754
12755         if (!TimeLeft && setup.time_limit)
12756           for (i = 0; i < MAX_PLAYERS; i++)
12757             KillPlayer(&stored_player[i]);
12758       }
12759       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12760       {
12761         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12762
12763         DisplayGameControlValues();
12764       }
12765     }
12766
12767     if (tape.single_step && tape.recording && !tape.pausing &&
12768         !player->programmed_action)
12769       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12770
12771     if (!player->programmed_action)
12772       CheckSaveEngineSnapshot(player);
12773   }
12774 }
12775
12776 void ScrollScreen(struct PlayerInfo *player, int mode)
12777 {
12778   static unsigned int screen_frame_counter = 0;
12779
12780   if (mode == SCROLL_INIT)
12781   {
12782     /* set scrolling step size according to actual player's moving speed */
12783     ScrollStepSize = TILEX / player->move_delay_value;
12784
12785     screen_frame_counter = FrameCounter;
12786     ScreenMovDir = player->MovDir;
12787     ScreenMovPos = player->MovPos;
12788     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12789     return;
12790   }
12791   else if (!FrameReached(&screen_frame_counter, 1))
12792     return;
12793
12794   if (ScreenMovPos)
12795   {
12796     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12797     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12798     redraw_mask |= REDRAW_FIELD;
12799   }
12800   else
12801     ScreenMovDir = MV_NONE;
12802 }
12803
12804 void TestIfPlayerTouchesCustomElement(int x, int y)
12805 {
12806   static int xy[4][2] =
12807   {
12808     { 0, -1 },
12809     { -1, 0 },
12810     { +1, 0 },
12811     { 0, +1 }
12812   };
12813   static int trigger_sides[4][2] =
12814   {
12815     /* center side       border side */
12816     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12817     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12818     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12819     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12820   };
12821   static int touch_dir[4] =
12822   {
12823     MV_LEFT | MV_RIGHT,
12824     MV_UP   | MV_DOWN,
12825     MV_UP   | MV_DOWN,
12826     MV_LEFT | MV_RIGHT
12827   };
12828   int center_element = Feld[x][y];      /* should always be non-moving! */
12829   int i;
12830
12831   for (i = 0; i < NUM_DIRECTIONS; i++)
12832   {
12833     int xx = x + xy[i][0];
12834     int yy = y + xy[i][1];
12835     int center_side = trigger_sides[i][0];
12836     int border_side = trigger_sides[i][1];
12837     int border_element;
12838
12839     if (!IN_LEV_FIELD(xx, yy))
12840       continue;
12841
12842     if (IS_PLAYER(x, y))                /* player found at center element */
12843     {
12844       struct PlayerInfo *player = PLAYERINFO(x, y);
12845
12846       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12847         border_element = Feld[xx][yy];          /* may be moving! */
12848       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12849         border_element = Feld[xx][yy];
12850       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12851         border_element = MovingOrBlocked2Element(xx, yy);
12852       else
12853         continue;               /* center and border element do not touch */
12854
12855       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12856                                  player->index_bit, border_side);
12857       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12858                                           CE_PLAYER_TOUCHES_X,
12859                                           player->index_bit, border_side);
12860
12861       {
12862         /* use player element that is initially defined in the level playfield,
12863            not the player element that corresponds to the runtime player number
12864            (example: a level that contains EL_PLAYER_3 as the only player would
12865            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12866         int player_element = PLAYERINFO(x, y)->initial_element;
12867
12868         CheckElementChangeBySide(xx, yy, border_element, player_element,
12869                                  CE_TOUCHING_X, border_side);
12870       }
12871     }
12872     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12873     {
12874       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12875
12876       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12877       {
12878         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12879           continue;             /* center and border element do not touch */
12880       }
12881
12882       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12883                                  player->index_bit, center_side);
12884       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12885                                           CE_PLAYER_TOUCHES_X,
12886                                           player->index_bit, center_side);
12887
12888       {
12889         /* use player element that is initially defined in the level playfield,
12890            not the player element that corresponds to the runtime player number
12891            (example: a level that contains EL_PLAYER_3 as the only player would
12892            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12893         int player_element = PLAYERINFO(xx, yy)->initial_element;
12894
12895         CheckElementChangeBySide(x, y, center_element, player_element,
12896                                  CE_TOUCHING_X, center_side);
12897       }
12898
12899       break;
12900     }
12901   }
12902 }
12903
12904 void TestIfElementTouchesCustomElement(int x, int y)
12905 {
12906   static int xy[4][2] =
12907   {
12908     { 0, -1 },
12909     { -1, 0 },
12910     { +1, 0 },
12911     { 0, +1 }
12912   };
12913   static int trigger_sides[4][2] =
12914   {
12915     /* center side      border side */
12916     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12917     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12918     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12919     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12920   };
12921   static int touch_dir[4] =
12922   {
12923     MV_LEFT | MV_RIGHT,
12924     MV_UP   | MV_DOWN,
12925     MV_UP   | MV_DOWN,
12926     MV_LEFT | MV_RIGHT
12927   };
12928   boolean change_center_element = FALSE;
12929   int center_element = Feld[x][y];      /* should always be non-moving! */
12930   int border_element_old[NUM_DIRECTIONS];
12931   int i;
12932
12933   for (i = 0; i < NUM_DIRECTIONS; i++)
12934   {
12935     int xx = x + xy[i][0];
12936     int yy = y + xy[i][1];
12937     int border_element;
12938
12939     border_element_old[i] = -1;
12940
12941     if (!IN_LEV_FIELD(xx, yy))
12942       continue;
12943
12944     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12945       border_element = Feld[xx][yy];    /* may be moving! */
12946     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12947       border_element = Feld[xx][yy];
12948     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12949       border_element = MovingOrBlocked2Element(xx, yy);
12950     else
12951       continue;                 /* center and border element do not touch */
12952
12953     border_element_old[i] = border_element;
12954   }
12955
12956   for (i = 0; i < NUM_DIRECTIONS; i++)
12957   {
12958     int xx = x + xy[i][0];
12959     int yy = y + xy[i][1];
12960     int center_side = trigger_sides[i][0];
12961     int border_element = border_element_old[i];
12962
12963     if (border_element == -1)
12964       continue;
12965
12966     /* check for change of border element */
12967     CheckElementChangeBySide(xx, yy, border_element, center_element,
12968                              CE_TOUCHING_X, center_side);
12969
12970     /* (center element cannot be player, so we dont have to check this here) */
12971   }
12972
12973   for (i = 0; i < NUM_DIRECTIONS; i++)
12974   {
12975     int xx = x + xy[i][0];
12976     int yy = y + xy[i][1];
12977     int border_side = trigger_sides[i][1];
12978     int border_element = border_element_old[i];
12979
12980     if (border_element == -1)
12981       continue;
12982
12983     /* check for change of center element (but change it only once) */
12984     if (!change_center_element)
12985       change_center_element =
12986         CheckElementChangeBySide(x, y, center_element, border_element,
12987                                  CE_TOUCHING_X, border_side);
12988
12989     if (IS_PLAYER(xx, yy))
12990     {
12991       /* use player element that is initially defined in the level playfield,
12992          not the player element that corresponds to the runtime player number
12993          (example: a level that contains EL_PLAYER_3 as the only player would
12994          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12995       int player_element = PLAYERINFO(xx, yy)->initial_element;
12996
12997       CheckElementChangeBySide(x, y, center_element, player_element,
12998                                CE_TOUCHING_X, border_side);
12999     }
13000   }
13001 }
13002
13003 void TestIfElementHitsCustomElement(int x, int y, int direction)
13004 {
13005   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13006   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13007   int hitx = x + dx, hity = y + dy;
13008   int hitting_element = Feld[x][y];
13009   int touched_element;
13010
13011   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13012     return;
13013
13014   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13015                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13016
13017   if (IN_LEV_FIELD(hitx, hity))
13018   {
13019     int opposite_direction = MV_DIR_OPPOSITE(direction);
13020     int hitting_side = direction;
13021     int touched_side = opposite_direction;
13022     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13023                           MovDir[hitx][hity] != direction ||
13024                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13025
13026     object_hit = TRUE;
13027
13028     if (object_hit)
13029     {
13030       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13031                                CE_HITTING_X, touched_side);
13032
13033       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13034                                CE_HIT_BY_X, hitting_side);
13035
13036       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13037                                CE_HIT_BY_SOMETHING, opposite_direction);
13038
13039       if (IS_PLAYER(hitx, hity))
13040       {
13041         /* use player element that is initially defined in the level playfield,
13042            not the player element that corresponds to the runtime player number
13043            (example: a level that contains EL_PLAYER_3 as the only player would
13044            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13045         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13046
13047         CheckElementChangeBySide(x, y, hitting_element, player_element,
13048                                  CE_HITTING_X, touched_side);
13049       }
13050     }
13051   }
13052
13053   /* "hitting something" is also true when hitting the playfield border */
13054   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13055                            CE_HITTING_SOMETHING, direction);
13056 }
13057
13058 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13059 {
13060   int i, kill_x = -1, kill_y = -1;
13061
13062   int bad_element = -1;
13063   static int test_xy[4][2] =
13064   {
13065     { 0, -1 },
13066     { -1, 0 },
13067     { +1, 0 },
13068     { 0, +1 }
13069   };
13070   static int test_dir[4] =
13071   {
13072     MV_UP,
13073     MV_LEFT,
13074     MV_RIGHT,
13075     MV_DOWN
13076   };
13077
13078   for (i = 0; i < NUM_DIRECTIONS; i++)
13079   {
13080     int test_x, test_y, test_move_dir, test_element;
13081
13082     test_x = good_x + test_xy[i][0];
13083     test_y = good_y + test_xy[i][1];
13084
13085     if (!IN_LEV_FIELD(test_x, test_y))
13086       continue;
13087
13088     test_move_dir =
13089       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13090
13091     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13092
13093     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13094        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13095     */
13096     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13097         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13098     {
13099       kill_x = test_x;
13100       kill_y = test_y;
13101       bad_element = test_element;
13102
13103       break;
13104     }
13105   }
13106
13107   if (kill_x != -1 || kill_y != -1)
13108   {
13109     if (IS_PLAYER(good_x, good_y))
13110     {
13111       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13112
13113       if (player->shield_deadly_time_left > 0 &&
13114           !IS_INDESTRUCTIBLE(bad_element))
13115         Bang(kill_x, kill_y);
13116       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13117         KillPlayer(player);
13118     }
13119     else
13120       Bang(good_x, good_y);
13121   }
13122 }
13123
13124 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13125 {
13126   int i, kill_x = -1, kill_y = -1;
13127   int bad_element = Feld[bad_x][bad_y];
13128   static int test_xy[4][2] =
13129   {
13130     { 0, -1 },
13131     { -1, 0 },
13132     { +1, 0 },
13133     { 0, +1 }
13134   };
13135   static int touch_dir[4] =
13136   {
13137     MV_LEFT | MV_RIGHT,
13138     MV_UP   | MV_DOWN,
13139     MV_UP   | MV_DOWN,
13140     MV_LEFT | MV_RIGHT
13141   };
13142   static int test_dir[4] =
13143   {
13144     MV_UP,
13145     MV_LEFT,
13146     MV_RIGHT,
13147     MV_DOWN
13148   };
13149
13150   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13151     return;
13152
13153   for (i = 0; i < NUM_DIRECTIONS; i++)
13154   {
13155     int test_x, test_y, test_move_dir, test_element;
13156
13157     test_x = bad_x + test_xy[i][0];
13158     test_y = bad_y + test_xy[i][1];
13159
13160     if (!IN_LEV_FIELD(test_x, test_y))
13161       continue;
13162
13163     test_move_dir =
13164       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13165
13166     test_element = Feld[test_x][test_y];
13167
13168     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13169        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13170     */
13171     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13172         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13173     {
13174       /* good thing is player or penguin that does not move away */
13175       if (IS_PLAYER(test_x, test_y))
13176       {
13177         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13178
13179         if (bad_element == EL_ROBOT && player->is_moving)
13180           continue;     /* robot does not kill player if he is moving */
13181
13182         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13183         {
13184           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13185             continue;           /* center and border element do not touch */
13186         }
13187
13188         kill_x = test_x;
13189         kill_y = test_y;
13190
13191         break;
13192       }
13193       else if (test_element == EL_PENGUIN)
13194       {
13195         kill_x = test_x;
13196         kill_y = test_y;
13197
13198         break;
13199       }
13200     }
13201   }
13202
13203   if (kill_x != -1 || kill_y != -1)
13204   {
13205     if (IS_PLAYER(kill_x, kill_y))
13206     {
13207       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13208
13209       if (player->shield_deadly_time_left > 0 &&
13210           !IS_INDESTRUCTIBLE(bad_element))
13211         Bang(bad_x, bad_y);
13212       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13213         KillPlayer(player);
13214     }
13215     else
13216       Bang(kill_x, kill_y);
13217   }
13218 }
13219
13220 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13221 {
13222   int bad_element = Feld[bad_x][bad_y];
13223   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13224   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13225   int test_x = bad_x + dx, test_y = bad_y + dy;
13226   int test_move_dir, test_element;
13227   int kill_x = -1, kill_y = -1;
13228
13229   if (!IN_LEV_FIELD(test_x, test_y))
13230     return;
13231
13232   test_move_dir =
13233     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13234
13235   test_element = Feld[test_x][test_y];
13236
13237   if (test_move_dir != bad_move_dir)
13238   {
13239     /* good thing can be player or penguin that does not move away */
13240     if (IS_PLAYER(test_x, test_y))
13241     {
13242       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13243
13244       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13245          player as being hit when he is moving towards the bad thing, because
13246          the "get hit by" condition would be lost after the player stops) */
13247       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13248         return;         /* player moves away from bad thing */
13249
13250       kill_x = test_x;
13251       kill_y = test_y;
13252     }
13253     else if (test_element == EL_PENGUIN)
13254     {
13255       kill_x = test_x;
13256       kill_y = test_y;
13257     }
13258   }
13259
13260   if (kill_x != -1 || kill_y != -1)
13261   {
13262     if (IS_PLAYER(kill_x, kill_y))
13263     {
13264       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13265
13266       if (player->shield_deadly_time_left > 0 &&
13267           !IS_INDESTRUCTIBLE(bad_element))
13268         Bang(bad_x, bad_y);
13269       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13270         KillPlayer(player);
13271     }
13272     else
13273       Bang(kill_x, kill_y);
13274   }
13275 }
13276
13277 void TestIfPlayerTouchesBadThing(int x, int y)
13278 {
13279   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13280 }
13281
13282 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13283 {
13284   TestIfGoodThingHitsBadThing(x, y, move_dir);
13285 }
13286
13287 void TestIfBadThingTouchesPlayer(int x, int y)
13288 {
13289   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13290 }
13291
13292 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13293 {
13294   TestIfBadThingHitsGoodThing(x, y, move_dir);
13295 }
13296
13297 void TestIfFriendTouchesBadThing(int x, int y)
13298 {
13299   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13300 }
13301
13302 void TestIfBadThingTouchesFriend(int x, int y)
13303 {
13304   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13305 }
13306
13307 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13308 {
13309   int i, kill_x = bad_x, kill_y = bad_y;
13310   static int xy[4][2] =
13311   {
13312     { 0, -1 },
13313     { -1, 0 },
13314     { +1, 0 },
13315     { 0, +1 }
13316   };
13317
13318   for (i = 0; i < NUM_DIRECTIONS; i++)
13319   {
13320     int x, y, element;
13321
13322     x = bad_x + xy[i][0];
13323     y = bad_y + xy[i][1];
13324     if (!IN_LEV_FIELD(x, y))
13325       continue;
13326
13327     element = Feld[x][y];
13328     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13329         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13330     {
13331       kill_x = x;
13332       kill_y = y;
13333       break;
13334     }
13335   }
13336
13337   if (kill_x != bad_x || kill_y != bad_y)
13338     Bang(bad_x, bad_y);
13339 }
13340
13341 void KillPlayer(struct PlayerInfo *player)
13342 {
13343   int jx = player->jx, jy = player->jy;
13344
13345   if (!player->active)
13346     return;
13347
13348 #if 0
13349   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13350          player->killed, player->active, player->reanimated);
13351 #endif
13352
13353   /* the following code was introduced to prevent an infinite loop when calling
13354      -> Bang()
13355      -> CheckTriggeredElementChangeExt()
13356      -> ExecuteCustomElementAction()
13357      -> KillPlayer()
13358      -> (infinitely repeating the above sequence of function calls)
13359      which occurs when killing the player while having a CE with the setting
13360      "kill player X when explosion of <player X>"; the solution using a new
13361      field "player->killed" was chosen for backwards compatibility, although
13362      clever use of the fields "player->active" etc. would probably also work */
13363 #if 1
13364   if (player->killed)
13365     return;
13366 #endif
13367
13368   player->killed = TRUE;
13369
13370   /* remove accessible field at the player's position */
13371   Feld[jx][jy] = EL_EMPTY;
13372
13373   /* deactivate shield (else Bang()/Explode() would not work right) */
13374   player->shield_normal_time_left = 0;
13375   player->shield_deadly_time_left = 0;
13376
13377 #if 0
13378   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13379          player->killed, player->active, player->reanimated);
13380 #endif
13381
13382   Bang(jx, jy);
13383
13384 #if 0
13385   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13386          player->killed, player->active, player->reanimated);
13387 #endif
13388
13389   if (player->reanimated)       /* killed player may have been reanimated */
13390     player->killed = player->reanimated = FALSE;
13391   else
13392     BuryPlayer(player);
13393 }
13394
13395 static void KillPlayerUnlessEnemyProtected(int x, int y)
13396 {
13397   if (!PLAYER_ENEMY_PROTECTED(x, y))
13398     KillPlayer(PLAYERINFO(x, y));
13399 }
13400
13401 static void KillPlayerUnlessExplosionProtected(int x, int y)
13402 {
13403   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13404     KillPlayer(PLAYERINFO(x, y));
13405 }
13406
13407 void BuryPlayer(struct PlayerInfo *player)
13408 {
13409   int jx = player->jx, jy = player->jy;
13410
13411   if (!player->active)
13412     return;
13413
13414   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13415   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13416
13417   player->GameOver = TRUE;
13418   RemovePlayer(player);
13419 }
13420
13421 void RemovePlayer(struct PlayerInfo *player)
13422 {
13423   int jx = player->jx, jy = player->jy;
13424   int i, found = FALSE;
13425
13426   player->present = FALSE;
13427   player->active = FALSE;
13428
13429   if (!ExplodeField[jx][jy])
13430     StorePlayer[jx][jy] = 0;
13431
13432   if (player->is_moving)
13433     TEST_DrawLevelField(player->last_jx, player->last_jy);
13434
13435   for (i = 0; i < MAX_PLAYERS; i++)
13436     if (stored_player[i].active)
13437       found = TRUE;
13438
13439   if (!found)
13440     AllPlayersGone = TRUE;
13441
13442   ExitX = ZX = jx;
13443   ExitY = ZY = jy;
13444 }
13445
13446 static void setFieldForSnapping(int x, int y, int element, int direction)
13447 {
13448   struct ElementInfo *ei = &element_info[element];
13449   int direction_bit = MV_DIR_TO_BIT(direction);
13450   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13451   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13452                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13453
13454   Feld[x][y] = EL_ELEMENT_SNAPPING;
13455   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13456
13457   ResetGfxAnimation(x, y);
13458
13459   GfxElement[x][y] = element;
13460   GfxAction[x][y] = action;
13461   GfxDir[x][y] = direction;
13462   GfxFrame[x][y] = -1;
13463 }
13464
13465 /*
13466   =============================================================================
13467   checkDiagonalPushing()
13468   -----------------------------------------------------------------------------
13469   check if diagonal input device direction results in pushing of object
13470   (by checking if the alternative direction is walkable, diggable, ...)
13471   =============================================================================
13472 */
13473
13474 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13475                                     int x, int y, int real_dx, int real_dy)
13476 {
13477   int jx, jy, dx, dy, xx, yy;
13478
13479   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13480     return TRUE;
13481
13482   /* diagonal direction: check alternative direction */
13483   jx = player->jx;
13484   jy = player->jy;
13485   dx = x - jx;
13486   dy = y - jy;
13487   xx = jx + (dx == 0 ? real_dx : 0);
13488   yy = jy + (dy == 0 ? real_dy : 0);
13489
13490   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13491 }
13492
13493 /*
13494   =============================================================================
13495   DigField()
13496   -----------------------------------------------------------------------------
13497   x, y:                 field next to player (non-diagonal) to try to dig to
13498   real_dx, real_dy:     direction as read from input device (can be diagonal)
13499   =============================================================================
13500 */
13501
13502 static int DigField(struct PlayerInfo *player,
13503                     int oldx, int oldy, int x, int y,
13504                     int real_dx, int real_dy, int mode)
13505 {
13506   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13507   boolean player_was_pushing = player->is_pushing;
13508   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13509   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13510   int jx = oldx, jy = oldy;
13511   int dx = x - jx, dy = y - jy;
13512   int nextx = x + dx, nexty = y + dy;
13513   int move_direction = (dx == -1 ? MV_LEFT  :
13514                         dx == +1 ? MV_RIGHT :
13515                         dy == -1 ? MV_UP    :
13516                         dy == +1 ? MV_DOWN  : MV_NONE);
13517   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13518   int dig_side = MV_DIR_OPPOSITE(move_direction);
13519   int old_element = Feld[jx][jy];
13520   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13521   int collect_count;
13522
13523   if (is_player)                /* function can also be called by EL_PENGUIN */
13524   {
13525     if (player->MovPos == 0)
13526     {
13527       player->is_digging = FALSE;
13528       player->is_collecting = FALSE;
13529     }
13530
13531     if (player->MovPos == 0)    /* last pushing move finished */
13532       player->is_pushing = FALSE;
13533
13534     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13535     {
13536       player->is_switching = FALSE;
13537       player->push_delay = -1;
13538
13539       return MP_NO_ACTION;
13540     }
13541   }
13542
13543   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13544     old_element = Back[jx][jy];
13545
13546   /* in case of element dropped at player position, check background */
13547   else if (Back[jx][jy] != EL_EMPTY &&
13548            game.engine_version >= VERSION_IDENT(2,2,0,0))
13549     old_element = Back[jx][jy];
13550
13551   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13552     return MP_NO_ACTION;        /* field has no opening in this direction */
13553
13554   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13555     return MP_NO_ACTION;        /* field has no opening in this direction */
13556
13557   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13558   {
13559     SplashAcid(x, y);
13560
13561     Feld[jx][jy] = player->artwork_element;
13562     InitMovingField(jx, jy, MV_DOWN);
13563     Store[jx][jy] = EL_ACID;
13564     ContinueMoving(jx, jy);
13565     BuryPlayer(player);
13566
13567     return MP_DONT_RUN_INTO;
13568   }
13569
13570   if (player_can_move && DONT_RUN_INTO(element))
13571   {
13572     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13573
13574     return MP_DONT_RUN_INTO;
13575   }
13576
13577   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13578     return MP_NO_ACTION;
13579
13580   collect_count = element_info[element].collect_count_initial;
13581
13582   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13583     return MP_NO_ACTION;
13584
13585   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13586     player_can_move = player_can_move_or_snap;
13587
13588   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13589       game.engine_version >= VERSION_IDENT(2,2,0,0))
13590   {
13591     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13592                                player->index_bit, dig_side);
13593     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13594                                         player->index_bit, dig_side);
13595
13596     if (element == EL_DC_LANDMINE)
13597       Bang(x, y);
13598
13599     if (Feld[x][y] != element)          /* field changed by snapping */
13600       return MP_ACTION;
13601
13602     return MP_NO_ACTION;
13603   }
13604
13605   if (player->gravity && is_player && !player->is_auto_moving &&
13606       canFallDown(player) && move_direction != MV_DOWN &&
13607       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13608     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13609
13610   if (player_can_move &&
13611       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13612   {
13613     int sound_element = SND_ELEMENT(element);
13614     int sound_action = ACTION_WALKING;
13615
13616     if (IS_RND_GATE(element))
13617     {
13618       if (!player->key[RND_GATE_NR(element)])
13619         return MP_NO_ACTION;
13620     }
13621     else if (IS_RND_GATE_GRAY(element))
13622     {
13623       if (!player->key[RND_GATE_GRAY_NR(element)])
13624         return MP_NO_ACTION;
13625     }
13626     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13627     {
13628       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13629         return MP_NO_ACTION;
13630     }
13631     else if (element == EL_EXIT_OPEN ||
13632              element == EL_EM_EXIT_OPEN ||
13633              element == EL_EM_EXIT_OPENING ||
13634              element == EL_STEEL_EXIT_OPEN ||
13635              element == EL_EM_STEEL_EXIT_OPEN ||
13636              element == EL_EM_STEEL_EXIT_OPENING ||
13637              element == EL_SP_EXIT_OPEN ||
13638              element == EL_SP_EXIT_OPENING)
13639     {
13640       sound_action = ACTION_PASSING;    /* player is passing exit */
13641     }
13642     else if (element == EL_EMPTY)
13643     {
13644       sound_action = ACTION_MOVING;             /* nothing to walk on */
13645     }
13646
13647     /* play sound from background or player, whatever is available */
13648     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13649       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13650     else
13651       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13652   }
13653   else if (player_can_move &&
13654            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13655   {
13656     if (!ACCESS_FROM(element, opposite_direction))
13657       return MP_NO_ACTION;      /* field not accessible from this direction */
13658
13659     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13660       return MP_NO_ACTION;
13661
13662     if (IS_EM_GATE(element))
13663     {
13664       if (!player->key[EM_GATE_NR(element)])
13665         return MP_NO_ACTION;
13666     }
13667     else if (IS_EM_GATE_GRAY(element))
13668     {
13669       if (!player->key[EM_GATE_GRAY_NR(element)])
13670         return MP_NO_ACTION;
13671     }
13672     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13673     {
13674       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13675         return MP_NO_ACTION;
13676     }
13677     else if (IS_EMC_GATE(element))
13678     {
13679       if (!player->key[EMC_GATE_NR(element)])
13680         return MP_NO_ACTION;
13681     }
13682     else if (IS_EMC_GATE_GRAY(element))
13683     {
13684       if (!player->key[EMC_GATE_GRAY_NR(element)])
13685         return MP_NO_ACTION;
13686     }
13687     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13688     {
13689       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13690         return MP_NO_ACTION;
13691     }
13692     else if (element == EL_DC_GATE_WHITE ||
13693              element == EL_DC_GATE_WHITE_GRAY ||
13694              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13695     {
13696       if (player->num_white_keys == 0)
13697         return MP_NO_ACTION;
13698
13699       player->num_white_keys--;
13700     }
13701     else if (IS_SP_PORT(element))
13702     {
13703       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13704           element == EL_SP_GRAVITY_PORT_RIGHT ||
13705           element == EL_SP_GRAVITY_PORT_UP ||
13706           element == EL_SP_GRAVITY_PORT_DOWN)
13707         player->gravity = !player->gravity;
13708       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13709                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13710                element == EL_SP_GRAVITY_ON_PORT_UP ||
13711                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13712         player->gravity = TRUE;
13713       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13714                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13715                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13716                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13717         player->gravity = FALSE;
13718     }
13719
13720     /* automatically move to the next field with double speed */
13721     player->programmed_action = move_direction;
13722
13723     if (player->move_delay_reset_counter == 0)
13724     {
13725       player->move_delay_reset_counter = 2;     /* two double speed steps */
13726
13727       DOUBLE_PLAYER_SPEED(player);
13728     }
13729
13730     PlayLevelSoundAction(x, y, ACTION_PASSING);
13731   }
13732   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13733   {
13734     RemoveField(x, y);
13735
13736     if (mode != DF_SNAP)
13737     {
13738       GfxElement[x][y] = GFX_ELEMENT(element);
13739       player->is_digging = TRUE;
13740     }
13741
13742     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13743
13744     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13745                                         player->index_bit, dig_side);
13746
13747     if (mode == DF_SNAP)
13748     {
13749       if (level.block_snap_field)
13750         setFieldForSnapping(x, y, element, move_direction);
13751       else
13752         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13753
13754       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13755                                           player->index_bit, dig_side);
13756     }
13757   }
13758   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13759   {
13760     RemoveField(x, y);
13761
13762     if (is_player && mode != DF_SNAP)
13763     {
13764       GfxElement[x][y] = element;
13765       player->is_collecting = TRUE;
13766     }
13767
13768     if (element == EL_SPEED_PILL)
13769     {
13770       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13771     }
13772     else if (element == EL_EXTRA_TIME && level.time > 0)
13773     {
13774       TimeLeft += level.extra_time;
13775
13776       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13777
13778       DisplayGameControlValues();
13779     }
13780     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13781     {
13782       player->shield_normal_time_left += level.shield_normal_time;
13783       if (element == EL_SHIELD_DEADLY)
13784         player->shield_deadly_time_left += level.shield_deadly_time;
13785     }
13786     else if (element == EL_DYNAMITE ||
13787              element == EL_EM_DYNAMITE ||
13788              element == EL_SP_DISK_RED)
13789     {
13790       if (player->inventory_size < MAX_INVENTORY_SIZE)
13791         player->inventory_element[player->inventory_size++] = element;
13792
13793       DrawGameDoorValues();
13794     }
13795     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13796     {
13797       player->dynabomb_count++;
13798       player->dynabombs_left++;
13799     }
13800     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13801     {
13802       player->dynabomb_size++;
13803     }
13804     else if (element == EL_DYNABOMB_INCREASE_POWER)
13805     {
13806       player->dynabomb_xl = TRUE;
13807     }
13808     else if (IS_KEY(element))
13809     {
13810       player->key[KEY_NR(element)] = TRUE;
13811
13812       DrawGameDoorValues();
13813     }
13814     else if (element == EL_DC_KEY_WHITE)
13815     {
13816       player->num_white_keys++;
13817
13818       /* display white keys? */
13819       /* DrawGameDoorValues(); */
13820     }
13821     else if (IS_ENVELOPE(element))
13822     {
13823       player->show_envelope = element;
13824     }
13825     else if (element == EL_EMC_LENSES)
13826     {
13827       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13828
13829       RedrawAllInvisibleElementsForLenses();
13830     }
13831     else if (element == EL_EMC_MAGNIFIER)
13832     {
13833       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13834
13835       RedrawAllInvisibleElementsForMagnifier();
13836     }
13837     else if (IS_DROPPABLE(element) ||
13838              IS_THROWABLE(element))     /* can be collected and dropped */
13839     {
13840       int i;
13841
13842       if (collect_count == 0)
13843         player->inventory_infinite_element = element;
13844       else
13845         for (i = 0; i < collect_count; i++)
13846           if (player->inventory_size < MAX_INVENTORY_SIZE)
13847             player->inventory_element[player->inventory_size++] = element;
13848
13849       DrawGameDoorValues();
13850     }
13851     else if (collect_count > 0)
13852     {
13853       local_player->gems_still_needed -= collect_count;
13854       if (local_player->gems_still_needed < 0)
13855         local_player->gems_still_needed = 0;
13856
13857       game.snapshot.collected_item = TRUE;
13858
13859       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13860
13861       DisplayGameControlValues();
13862     }
13863
13864     RaiseScoreElement(element);
13865     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13866
13867     if (is_player)
13868       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13869                                           player->index_bit, dig_side);
13870
13871     if (mode == DF_SNAP)
13872     {
13873       if (level.block_snap_field)
13874         setFieldForSnapping(x, y, element, move_direction);
13875       else
13876         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13877
13878       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13879                                           player->index_bit, dig_side);
13880     }
13881   }
13882   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13883   {
13884     if (mode == DF_SNAP && element != EL_BD_ROCK)
13885       return MP_NO_ACTION;
13886
13887     if (CAN_FALL(element) && dy)
13888       return MP_NO_ACTION;
13889
13890     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13891         !(element == EL_SPRING && level.use_spring_bug))
13892       return MP_NO_ACTION;
13893
13894     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13895         ((move_direction & MV_VERTICAL &&
13896           ((element_info[element].move_pattern & MV_LEFT &&
13897             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13898            (element_info[element].move_pattern & MV_RIGHT &&
13899             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13900          (move_direction & MV_HORIZONTAL &&
13901           ((element_info[element].move_pattern & MV_UP &&
13902             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13903            (element_info[element].move_pattern & MV_DOWN &&
13904             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13905       return MP_NO_ACTION;
13906
13907     /* do not push elements already moving away faster than player */
13908     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13909         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13910       return MP_NO_ACTION;
13911
13912     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13913     {
13914       if (player->push_delay_value == -1 || !player_was_pushing)
13915         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13916     }
13917     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13918     {
13919       if (player->push_delay_value == -1)
13920         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13921     }
13922     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13923     {
13924       if (!player->is_pushing)
13925         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13926     }
13927
13928     player->is_pushing = TRUE;
13929     player->is_active = TRUE;
13930
13931     if (!(IN_LEV_FIELD(nextx, nexty) &&
13932           (IS_FREE(nextx, nexty) ||
13933            (IS_SB_ELEMENT(element) &&
13934             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13935            (IS_CUSTOM_ELEMENT(element) &&
13936             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13937       return MP_NO_ACTION;
13938
13939     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13940       return MP_NO_ACTION;
13941
13942     if (player->push_delay == -1)       /* new pushing; restart delay */
13943       player->push_delay = 0;
13944
13945     if (player->push_delay < player->push_delay_value &&
13946         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13947         element != EL_SPRING && element != EL_BALLOON)
13948     {
13949       /* make sure that there is no move delay before next try to push */
13950       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13951         player->move_delay = 0;
13952
13953       return MP_NO_ACTION;
13954     }
13955
13956     if (IS_CUSTOM_ELEMENT(element) &&
13957         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13958     {
13959       if (!DigFieldByCE(nextx, nexty, element))
13960         return MP_NO_ACTION;
13961     }
13962
13963     if (IS_SB_ELEMENT(element))
13964     {
13965       if (element == EL_SOKOBAN_FIELD_FULL)
13966       {
13967         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13968         local_player->sokobanfields_still_needed++;
13969       }
13970
13971       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13972       {
13973         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13974         local_player->sokobanfields_still_needed--;
13975       }
13976
13977       Feld[x][y] = EL_SOKOBAN_OBJECT;
13978
13979       if (Back[x][y] == Back[nextx][nexty])
13980         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13981       else if (Back[x][y] != 0)
13982         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13983                                     ACTION_EMPTYING);
13984       else
13985         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13986                                     ACTION_FILLING);
13987
13988       if (local_player->sokobanfields_still_needed == 0 &&
13989           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13990       {
13991         PlayerWins(player);
13992
13993         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13994       }
13995     }
13996     else
13997       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13998
13999     InitMovingField(x, y, move_direction);
14000     GfxAction[x][y] = ACTION_PUSHING;
14001
14002     if (mode == DF_SNAP)
14003       ContinueMoving(x, y);
14004     else
14005       MovPos[x][y] = (dx != 0 ? dx : dy);
14006
14007     Pushed[x][y] = TRUE;
14008     Pushed[nextx][nexty] = TRUE;
14009
14010     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14011       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14012     else
14013       player->push_delay_value = -1;    /* get new value later */
14014
14015     /* check for element change _after_ element has been pushed */
14016     if (game.use_change_when_pushing_bug)
14017     {
14018       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14019                                  player->index_bit, dig_side);
14020       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14021                                           player->index_bit, dig_side);
14022     }
14023   }
14024   else if (IS_SWITCHABLE(element))
14025   {
14026     if (PLAYER_SWITCHING(player, x, y))
14027     {
14028       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14029                                           player->index_bit, dig_side);
14030
14031       return MP_ACTION;
14032     }
14033
14034     player->is_switching = TRUE;
14035     player->switch_x = x;
14036     player->switch_y = y;
14037
14038     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14039
14040     if (element == EL_ROBOT_WHEEL)
14041     {
14042       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14043       ZX = x;
14044       ZY = y;
14045
14046       game.robot_wheel_active = TRUE;
14047
14048       TEST_DrawLevelField(x, y);
14049     }
14050     else if (element == EL_SP_TERMINAL)
14051     {
14052       int xx, yy;
14053
14054       SCAN_PLAYFIELD(xx, yy)
14055       {
14056         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14057         {
14058           Bang(xx, yy);
14059         }
14060         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14061         {
14062           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14063
14064           ResetGfxAnimation(xx, yy);
14065           TEST_DrawLevelField(xx, yy);
14066         }
14067       }
14068     }
14069     else if (IS_BELT_SWITCH(element))
14070     {
14071       ToggleBeltSwitch(x, y);
14072     }
14073     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14074              element == EL_SWITCHGATE_SWITCH_DOWN ||
14075              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14076              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14077     {
14078       ToggleSwitchgateSwitch(x, y);
14079     }
14080     else if (element == EL_LIGHT_SWITCH ||
14081              element == EL_LIGHT_SWITCH_ACTIVE)
14082     {
14083       ToggleLightSwitch(x, y);
14084     }
14085     else if (element == EL_TIMEGATE_SWITCH ||
14086              element == EL_DC_TIMEGATE_SWITCH)
14087     {
14088       ActivateTimegateSwitch(x, y);
14089     }
14090     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14091              element == EL_BALLOON_SWITCH_RIGHT ||
14092              element == EL_BALLOON_SWITCH_UP    ||
14093              element == EL_BALLOON_SWITCH_DOWN  ||
14094              element == EL_BALLOON_SWITCH_NONE  ||
14095              element == EL_BALLOON_SWITCH_ANY)
14096     {
14097       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14098                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14099                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14100                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14101                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14102                              move_direction);
14103     }
14104     else if (element == EL_LAMP)
14105     {
14106       Feld[x][y] = EL_LAMP_ACTIVE;
14107       local_player->lights_still_needed--;
14108
14109       ResetGfxAnimation(x, y);
14110       TEST_DrawLevelField(x, y);
14111     }
14112     else if (element == EL_TIME_ORB_FULL)
14113     {
14114       Feld[x][y] = EL_TIME_ORB_EMPTY;
14115
14116       if (level.time > 0 || level.use_time_orb_bug)
14117       {
14118         TimeLeft += level.time_orb_time;
14119         game.no_time_limit = FALSE;
14120
14121         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14122
14123         DisplayGameControlValues();
14124       }
14125
14126       ResetGfxAnimation(x, y);
14127       TEST_DrawLevelField(x, y);
14128     }
14129     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14130              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14131     {
14132       int xx, yy;
14133
14134       game.ball_state = !game.ball_state;
14135
14136       SCAN_PLAYFIELD(xx, yy)
14137       {
14138         int e = Feld[xx][yy];
14139
14140         if (game.ball_state)
14141         {
14142           if (e == EL_EMC_MAGIC_BALL)
14143             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14144           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14145             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14146         }
14147         else
14148         {
14149           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14150             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14151           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14152             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14153         }
14154       }
14155     }
14156
14157     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14158                                         player->index_bit, dig_side);
14159
14160     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14161                                         player->index_bit, dig_side);
14162
14163     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14164                                         player->index_bit, dig_side);
14165
14166     return MP_ACTION;
14167   }
14168   else
14169   {
14170     if (!PLAYER_SWITCHING(player, x, y))
14171     {
14172       player->is_switching = TRUE;
14173       player->switch_x = x;
14174       player->switch_y = y;
14175
14176       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14177                                  player->index_bit, dig_side);
14178       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14179                                           player->index_bit, dig_side);
14180
14181       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14182                                  player->index_bit, dig_side);
14183       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14184                                           player->index_bit, dig_side);
14185     }
14186
14187     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14188                                player->index_bit, dig_side);
14189     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14190                                         player->index_bit, dig_side);
14191
14192     return MP_NO_ACTION;
14193   }
14194
14195   player->push_delay = -1;
14196
14197   if (is_player)                /* function can also be called by EL_PENGUIN */
14198   {
14199     if (Feld[x][y] != element)          /* really digged/collected something */
14200     {
14201       player->is_collecting = !player->is_digging;
14202       player->is_active = TRUE;
14203     }
14204   }
14205
14206   return MP_MOVING;
14207 }
14208
14209 static boolean DigFieldByCE(int x, int y, int digging_element)
14210 {
14211   int element = Feld[x][y];
14212
14213   if (!IS_FREE(x, y))
14214   {
14215     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14216                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14217                   ACTION_BREAKING);
14218
14219     /* no element can dig solid indestructible elements */
14220     if (IS_INDESTRUCTIBLE(element) &&
14221         !IS_DIGGABLE(element) &&
14222         !IS_COLLECTIBLE(element))
14223       return FALSE;
14224
14225     if (AmoebaNr[x][y] &&
14226         (element == EL_AMOEBA_FULL ||
14227          element == EL_BD_AMOEBA ||
14228          element == EL_AMOEBA_GROWING))
14229     {
14230       AmoebaCnt[AmoebaNr[x][y]]--;
14231       AmoebaCnt2[AmoebaNr[x][y]]--;
14232     }
14233
14234     if (IS_MOVING(x, y))
14235       RemoveMovingField(x, y);
14236     else
14237     {
14238       RemoveField(x, y);
14239       TEST_DrawLevelField(x, y);
14240     }
14241
14242     /* if digged element was about to explode, prevent the explosion */
14243     ExplodeField[x][y] = EX_TYPE_NONE;
14244
14245     PlayLevelSoundAction(x, y, action);
14246   }
14247
14248   Store[x][y] = EL_EMPTY;
14249
14250   /* this makes it possible to leave the removed element again */
14251   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14252     Store[x][y] = element;
14253
14254   return TRUE;
14255 }
14256
14257 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14258 {
14259   int jx = player->jx, jy = player->jy;
14260   int x = jx + dx, y = jy + dy;
14261   int snap_direction = (dx == -1 ? MV_LEFT  :
14262                         dx == +1 ? MV_RIGHT :
14263                         dy == -1 ? MV_UP    :
14264                         dy == +1 ? MV_DOWN  : MV_NONE);
14265   boolean can_continue_snapping = (level.continuous_snapping &&
14266                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14267
14268   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14269     return FALSE;
14270
14271   if (!player->active || !IN_LEV_FIELD(x, y))
14272     return FALSE;
14273
14274   if (dx && dy)
14275     return FALSE;
14276
14277   if (!dx && !dy)
14278   {
14279     if (player->MovPos == 0)
14280       player->is_pushing = FALSE;
14281
14282     player->is_snapping = FALSE;
14283
14284     if (player->MovPos == 0)
14285     {
14286       player->is_moving = FALSE;
14287       player->is_digging = FALSE;
14288       player->is_collecting = FALSE;
14289     }
14290
14291     return FALSE;
14292   }
14293
14294   /* prevent snapping with already pressed snap key when not allowed */
14295   if (player->is_snapping && !can_continue_snapping)
14296     return FALSE;
14297
14298   player->MovDir = snap_direction;
14299
14300   if (player->MovPos == 0)
14301   {
14302     player->is_moving = FALSE;
14303     player->is_digging = FALSE;
14304     player->is_collecting = FALSE;
14305   }
14306
14307   player->is_dropping = FALSE;
14308   player->is_dropping_pressed = FALSE;
14309   player->drop_pressed_delay = 0;
14310
14311   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14312     return FALSE;
14313
14314   player->is_snapping = TRUE;
14315   player->is_active = TRUE;
14316
14317   if (player->MovPos == 0)
14318   {
14319     player->is_moving = FALSE;
14320     player->is_digging = FALSE;
14321     player->is_collecting = FALSE;
14322   }
14323
14324   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14325     TEST_DrawLevelField(player->last_jx, player->last_jy);
14326
14327   TEST_DrawLevelField(x, y);
14328
14329   return TRUE;
14330 }
14331
14332 static boolean DropElement(struct PlayerInfo *player)
14333 {
14334   int old_element, new_element;
14335   int dropx = player->jx, dropy = player->jy;
14336   int drop_direction = player->MovDir;
14337   int drop_side = drop_direction;
14338   int drop_element = get_next_dropped_element(player);
14339
14340   /* do not drop an element on top of another element; when holding drop key
14341      pressed without moving, dropped element must move away before the next
14342      element can be dropped (this is especially important if the next element
14343      is dynamite, which can be placed on background for historical reasons) */
14344   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14345     return MP_ACTION;
14346
14347   if (IS_THROWABLE(drop_element))
14348   {
14349     dropx += GET_DX_FROM_DIR(drop_direction);
14350     dropy += GET_DY_FROM_DIR(drop_direction);
14351
14352     if (!IN_LEV_FIELD(dropx, dropy))
14353       return FALSE;
14354   }
14355
14356   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14357   new_element = drop_element;           /* default: no change when dropping */
14358
14359   /* check if player is active, not moving and ready to drop */
14360   if (!player->active || player->MovPos || player->drop_delay > 0)
14361     return FALSE;
14362
14363   /* check if player has anything that can be dropped */
14364   if (new_element == EL_UNDEFINED)
14365     return FALSE;
14366
14367   /* only set if player has anything that can be dropped */
14368   player->is_dropping_pressed = TRUE;
14369
14370   /* check if drop key was pressed long enough for EM style dynamite */
14371   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14372     return FALSE;
14373
14374   /* check if anything can be dropped at the current position */
14375   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14376     return FALSE;
14377
14378   /* collected custom elements can only be dropped on empty fields */
14379   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14380     return FALSE;
14381
14382   if (old_element != EL_EMPTY)
14383     Back[dropx][dropy] = old_element;   /* store old element on this field */
14384
14385   ResetGfxAnimation(dropx, dropy);
14386   ResetRandomAnimationValue(dropx, dropy);
14387
14388   if (player->inventory_size > 0 ||
14389       player->inventory_infinite_element != EL_UNDEFINED)
14390   {
14391     if (player->inventory_size > 0)
14392     {
14393       player->inventory_size--;
14394
14395       DrawGameDoorValues();
14396
14397       if (new_element == EL_DYNAMITE)
14398         new_element = EL_DYNAMITE_ACTIVE;
14399       else if (new_element == EL_EM_DYNAMITE)
14400         new_element = EL_EM_DYNAMITE_ACTIVE;
14401       else if (new_element == EL_SP_DISK_RED)
14402         new_element = EL_SP_DISK_RED_ACTIVE;
14403     }
14404
14405     Feld[dropx][dropy] = new_element;
14406
14407     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14408       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14409                           el2img(Feld[dropx][dropy]), 0);
14410
14411     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14412
14413     /* needed if previous element just changed to "empty" in the last frame */
14414     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14415
14416     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14417                                player->index_bit, drop_side);
14418     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14419                                         CE_PLAYER_DROPS_X,
14420                                         player->index_bit, drop_side);
14421
14422     TestIfElementTouchesCustomElement(dropx, dropy);
14423   }
14424   else          /* player is dropping a dyna bomb */
14425   {
14426     player->dynabombs_left--;
14427
14428     Feld[dropx][dropy] = new_element;
14429
14430     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14431       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14432                           el2img(Feld[dropx][dropy]), 0);
14433
14434     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14435   }
14436
14437   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14438     InitField_WithBug1(dropx, dropy, FALSE);
14439
14440   new_element = Feld[dropx][dropy];     /* element might have changed */
14441
14442   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14443       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14444   {
14445     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14446       MovDir[dropx][dropy] = drop_direction;
14447
14448     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14449
14450     /* do not cause impact style collision by dropping elements that can fall */
14451     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14452   }
14453
14454   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14455   player->is_dropping = TRUE;
14456
14457   player->drop_pressed_delay = 0;
14458   player->is_dropping_pressed = FALSE;
14459
14460   player->drop_x = dropx;
14461   player->drop_y = dropy;
14462
14463   return TRUE;
14464 }
14465
14466 /* ------------------------------------------------------------------------- */
14467 /* game sound playing functions                                              */
14468 /* ------------------------------------------------------------------------- */
14469
14470 static int *loop_sound_frame = NULL;
14471 static int *loop_sound_volume = NULL;
14472
14473 void InitPlayLevelSound()
14474 {
14475   int num_sounds = getSoundListSize();
14476
14477   checked_free(loop_sound_frame);
14478   checked_free(loop_sound_volume);
14479
14480   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14481   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14482 }
14483
14484 static void PlayLevelSound(int x, int y, int nr)
14485 {
14486   int sx = SCREENX(x), sy = SCREENY(y);
14487   int volume, stereo_position;
14488   int max_distance = 8;
14489   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14490
14491   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14492       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14493     return;
14494
14495   if (!IN_LEV_FIELD(x, y) ||
14496       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14497       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14498     return;
14499
14500   volume = SOUND_MAX_VOLUME;
14501
14502   if (!IN_SCR_FIELD(sx, sy))
14503   {
14504     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14505     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14506
14507     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14508   }
14509
14510   stereo_position = (SOUND_MAX_LEFT +
14511                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14512                      (SCR_FIELDX + 2 * max_distance));
14513
14514   if (IS_LOOP_SOUND(nr))
14515   {
14516     /* This assures that quieter loop sounds do not overwrite louder ones,
14517        while restarting sound volume comparison with each new game frame. */
14518
14519     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14520       return;
14521
14522     loop_sound_volume[nr] = volume;
14523     loop_sound_frame[nr] = FrameCounter;
14524   }
14525
14526   PlaySoundExt(nr, volume, stereo_position, type);
14527 }
14528
14529 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14530 {
14531   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14532                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14533                  y < LEVELY(BY1) ? LEVELY(BY1) :
14534                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14535                  sound_action);
14536 }
14537
14538 static void PlayLevelSoundAction(int x, int y, int action)
14539 {
14540   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14541 }
14542
14543 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14544 {
14545   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14546
14547   if (sound_effect != SND_UNDEFINED)
14548     PlayLevelSound(x, y, sound_effect);
14549 }
14550
14551 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14552                                               int action)
14553 {
14554   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14555
14556   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14557     PlayLevelSound(x, y, sound_effect);
14558 }
14559
14560 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14561 {
14562   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14563
14564   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14565     PlayLevelSound(x, y, sound_effect);
14566 }
14567
14568 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14569 {
14570   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14571
14572   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14573     StopSound(sound_effect);
14574 }
14575
14576 static int getLevelMusicNr()
14577 {
14578   if (levelset.music[level_nr] != MUS_UNDEFINED)
14579     return levelset.music[level_nr];            /* from config file */
14580   else
14581     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14582 }
14583
14584 static void FadeLevelSounds()
14585 {
14586   FadeSounds();
14587 }
14588
14589 static void FadeLevelMusic()
14590 {
14591   int music_nr = getLevelMusicNr();
14592   char *curr_music = getCurrentlyPlayingMusicFilename();
14593   char *next_music = getMusicInfoEntryFilename(music_nr);
14594
14595   if (!strEqual(curr_music, next_music))
14596     FadeMusic();
14597 }
14598
14599 void FadeLevelSoundsAndMusic()
14600 {
14601   FadeLevelSounds();
14602   FadeLevelMusic();
14603 }
14604
14605 static void PlayLevelMusic()
14606 {
14607   int music_nr = getLevelMusicNr();
14608   char *curr_music = getCurrentlyPlayingMusicFilename();
14609   char *next_music = getMusicInfoEntryFilename(music_nr);
14610
14611   if (!strEqual(curr_music, next_music))
14612     PlayMusic(music_nr);
14613 }
14614
14615 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14616 {
14617   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14618   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14619   int x = xx - 1 - offset;
14620   int y = yy - 1 - offset;
14621
14622   switch (sample)
14623   {
14624     case SAMPLE_blank:
14625       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14626       break;
14627
14628     case SAMPLE_roll:
14629       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14630       break;
14631
14632     case SAMPLE_stone:
14633       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14634       break;
14635
14636     case SAMPLE_nut:
14637       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14638       break;
14639
14640     case SAMPLE_crack:
14641       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14642       break;
14643
14644     case SAMPLE_bug:
14645       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14646       break;
14647
14648     case SAMPLE_tank:
14649       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14650       break;
14651
14652     case SAMPLE_android_clone:
14653       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14654       break;
14655
14656     case SAMPLE_android_move:
14657       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14658       break;
14659
14660     case SAMPLE_spring:
14661       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14662       break;
14663
14664     case SAMPLE_slurp:
14665       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14666       break;
14667
14668     case SAMPLE_eater:
14669       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14670       break;
14671
14672     case SAMPLE_eater_eat:
14673       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14674       break;
14675
14676     case SAMPLE_alien:
14677       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14678       break;
14679
14680     case SAMPLE_collect:
14681       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14682       break;
14683
14684     case SAMPLE_diamond:
14685       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14686       break;
14687
14688     case SAMPLE_squash:
14689       /* !!! CHECK THIS !!! */
14690 #if 1
14691       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14692 #else
14693       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14694 #endif
14695       break;
14696
14697     case SAMPLE_wonderfall:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14699       break;
14700
14701     case SAMPLE_drip:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14703       break;
14704
14705     case SAMPLE_push:
14706       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14707       break;
14708
14709     case SAMPLE_dirt:
14710       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14711       break;
14712
14713     case SAMPLE_acid:
14714       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14715       break;
14716
14717     case SAMPLE_ball:
14718       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14719       break;
14720
14721     case SAMPLE_grow:
14722       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14723       break;
14724
14725     case SAMPLE_wonder:
14726       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14727       break;
14728
14729     case SAMPLE_door:
14730       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14731       break;
14732
14733     case SAMPLE_exit_open:
14734       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14735       break;
14736
14737     case SAMPLE_exit_leave:
14738       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14739       break;
14740
14741     case SAMPLE_dynamite:
14742       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14743       break;
14744
14745     case SAMPLE_tick:
14746       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14747       break;
14748
14749     case SAMPLE_press:
14750       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14751       break;
14752
14753     case SAMPLE_wheel:
14754       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14755       break;
14756
14757     case SAMPLE_boom:
14758       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14759       break;
14760
14761     case SAMPLE_die:
14762       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14763       break;
14764
14765     case SAMPLE_time:
14766       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14767       break;
14768
14769     default:
14770       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14771       break;
14772   }
14773 }
14774
14775 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14776 {
14777   int element = map_element_SP_to_RND(element_sp);
14778   int action = map_action_SP_to_RND(action_sp);
14779   int offset = (setup.sp_show_border_elements ? 0 : 1);
14780   int x = xx - offset;
14781   int y = yy - offset;
14782
14783   PlayLevelSoundElementAction(x, y, element, action);
14784 }
14785
14786 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14787 {
14788   int element = map_element_MM_to_RND(element_mm);
14789   int action = map_action_MM_to_RND(action_mm);
14790   int offset = 0;
14791   int x = xx - offset;
14792   int y = yy - offset;
14793
14794   if (!IS_MM_ELEMENT(element))
14795     element = EL_MM_DEFAULT;
14796
14797   PlayLevelSoundElementAction(x, y, element, action);
14798 }
14799
14800 void PlaySound_MM(int sound_mm)
14801 {
14802   int sound = map_sound_MM_to_RND(sound_mm);
14803
14804   if (sound == SND_UNDEFINED)
14805     return;
14806
14807   PlaySound(sound);
14808 }
14809
14810 void PlaySoundLoop_MM(int sound_mm)
14811 {
14812   int sound = map_sound_MM_to_RND(sound_mm);
14813
14814   if (sound == SND_UNDEFINED)
14815     return;
14816
14817   PlaySoundLoop(sound);
14818 }
14819
14820 void StopSound_MM(int sound_mm)
14821 {
14822   int sound = map_sound_MM_to_RND(sound_mm);
14823
14824   if (sound == SND_UNDEFINED)
14825     return;
14826
14827   StopSound(sound);
14828 }
14829
14830 void RaiseScore(int value)
14831 {
14832   local_player->score += value;
14833
14834   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14835
14836   DisplayGameControlValues();
14837 }
14838
14839 void RaiseScoreElement(int element)
14840 {
14841   switch (element)
14842   {
14843     case EL_EMERALD:
14844     case EL_BD_DIAMOND:
14845     case EL_EMERALD_YELLOW:
14846     case EL_EMERALD_RED:
14847     case EL_EMERALD_PURPLE:
14848     case EL_SP_INFOTRON:
14849       RaiseScore(level.score[SC_EMERALD]);
14850       break;
14851     case EL_DIAMOND:
14852       RaiseScore(level.score[SC_DIAMOND]);
14853       break;
14854     case EL_CRYSTAL:
14855       RaiseScore(level.score[SC_CRYSTAL]);
14856       break;
14857     case EL_PEARL:
14858       RaiseScore(level.score[SC_PEARL]);
14859       break;
14860     case EL_BUG:
14861     case EL_BD_BUTTERFLY:
14862     case EL_SP_ELECTRON:
14863       RaiseScore(level.score[SC_BUG]);
14864       break;
14865     case EL_SPACESHIP:
14866     case EL_BD_FIREFLY:
14867     case EL_SP_SNIKSNAK:
14868       RaiseScore(level.score[SC_SPACESHIP]);
14869       break;
14870     case EL_YAMYAM:
14871     case EL_DARK_YAMYAM:
14872       RaiseScore(level.score[SC_YAMYAM]);
14873       break;
14874     case EL_ROBOT:
14875       RaiseScore(level.score[SC_ROBOT]);
14876       break;
14877     case EL_PACMAN:
14878       RaiseScore(level.score[SC_PACMAN]);
14879       break;
14880     case EL_NUT:
14881       RaiseScore(level.score[SC_NUT]);
14882       break;
14883     case EL_DYNAMITE:
14884     case EL_EM_DYNAMITE:
14885     case EL_SP_DISK_RED:
14886     case EL_DYNABOMB_INCREASE_NUMBER:
14887     case EL_DYNABOMB_INCREASE_SIZE:
14888     case EL_DYNABOMB_INCREASE_POWER:
14889       RaiseScore(level.score[SC_DYNAMITE]);
14890       break;
14891     case EL_SHIELD_NORMAL:
14892     case EL_SHIELD_DEADLY:
14893       RaiseScore(level.score[SC_SHIELD]);
14894       break;
14895     case EL_EXTRA_TIME:
14896       RaiseScore(level.extra_time_score);
14897       break;
14898     case EL_KEY_1:
14899     case EL_KEY_2:
14900     case EL_KEY_3:
14901     case EL_KEY_4:
14902     case EL_EM_KEY_1:
14903     case EL_EM_KEY_2:
14904     case EL_EM_KEY_3:
14905     case EL_EM_KEY_4:
14906     case EL_EMC_KEY_5:
14907     case EL_EMC_KEY_6:
14908     case EL_EMC_KEY_7:
14909     case EL_EMC_KEY_8:
14910     case EL_DC_KEY_WHITE:
14911       RaiseScore(level.score[SC_KEY]);
14912       break;
14913     default:
14914       RaiseScore(element_info[element].collect_score);
14915       break;
14916   }
14917 }
14918
14919 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14920 {
14921   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14922   {
14923     /* closing door required in case of envelope style request dialogs */
14924     if (!skip_request)
14925       CloseDoor(DOOR_CLOSE_1);
14926
14927 #if defined(NETWORK_AVALIABLE)
14928     if (options.network)
14929       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14930     else
14931 #endif
14932     {
14933       if (quick_quit)
14934         FadeSkipNextFadeIn();
14935
14936       SetGameStatus(GAME_MODE_MAIN);
14937
14938       DrawMainMenu();
14939     }
14940   }
14941   else          /* continue playing the game */
14942   {
14943     if (tape.playing && tape.deactivate_display)
14944       TapeDeactivateDisplayOff(TRUE);
14945
14946     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14947
14948     if (tape.playing && tape.deactivate_display)
14949       TapeDeactivateDisplayOn();
14950   }
14951 }
14952
14953 void RequestQuitGame(boolean ask_if_really_quit)
14954 {
14955   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14956   boolean skip_request = AllPlayersGone || quick_quit;
14957
14958   RequestQuitGameExt(skip_request, quick_quit,
14959                      "Do you really want to quit the game?");
14960 }
14961
14962 void RequestRestartGame(char *message)
14963 {
14964   game.restart_game_message = NULL;
14965
14966   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14967   {
14968     StartGameActions(options.network, setup.autorecord, level.random_seed);
14969   }
14970   else
14971   {
14972     SetGameStatus(GAME_MODE_MAIN);
14973
14974     DrawMainMenu();
14975   }
14976 }
14977
14978
14979 /* ------------------------------------------------------------------------- */
14980 /* random generator functions                                                */
14981 /* ------------------------------------------------------------------------- */
14982
14983 unsigned int InitEngineRandom_RND(int seed)
14984 {
14985   game.num_random_calls = 0;
14986
14987   return InitEngineRandom(seed);
14988 }
14989
14990 unsigned int RND(int max)
14991 {
14992   if (max > 0)
14993   {
14994     game.num_random_calls++;
14995
14996     return GetEngineRandom(max);
14997   }
14998
14999   return 0;
15000 }
15001
15002
15003 /* ------------------------------------------------------------------------- */
15004 /* game engine snapshot handling functions                                   */
15005 /* ------------------------------------------------------------------------- */
15006
15007 struct EngineSnapshotInfo
15008 {
15009   /* runtime values for custom element collect score */
15010   int collect_score[NUM_CUSTOM_ELEMENTS];
15011
15012   /* runtime values for group element choice position */
15013   int choice_pos[NUM_GROUP_ELEMENTS];
15014
15015   /* runtime values for belt position animations */
15016   int belt_graphic[4][NUM_BELT_PARTS];
15017   int belt_anim_mode[4][NUM_BELT_PARTS];
15018 };
15019
15020 static struct EngineSnapshotInfo engine_snapshot_rnd;
15021 static char *snapshot_level_identifier = NULL;
15022 static int snapshot_level_nr = -1;
15023
15024 static void SaveEngineSnapshotValues_RND()
15025 {
15026   static int belt_base_active_element[4] =
15027   {
15028     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15029     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15030     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15031     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15032   };
15033   int i, j;
15034
15035   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15036   {
15037     int element = EL_CUSTOM_START + i;
15038
15039     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15040   }
15041
15042   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15043   {
15044     int element = EL_GROUP_START + i;
15045
15046     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15047   }
15048
15049   for (i = 0; i < 4; i++)
15050   {
15051     for (j = 0; j < NUM_BELT_PARTS; j++)
15052     {
15053       int element = belt_base_active_element[i] + j;
15054       int graphic = el2img(element);
15055       int anim_mode = graphic_info[graphic].anim_mode;
15056
15057       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15058       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15059     }
15060   }
15061 }
15062
15063 static void LoadEngineSnapshotValues_RND()
15064 {
15065   unsigned int num_random_calls = game.num_random_calls;
15066   int i, j;
15067
15068   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15069   {
15070     int element = EL_CUSTOM_START + i;
15071
15072     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15073   }
15074
15075   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15076   {
15077     int element = EL_GROUP_START + i;
15078
15079     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15080   }
15081
15082   for (i = 0; i < 4; i++)
15083   {
15084     for (j = 0; j < NUM_BELT_PARTS; j++)
15085     {
15086       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15087       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15088
15089       graphic_info[graphic].anim_mode = anim_mode;
15090     }
15091   }
15092
15093   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15094   {
15095     InitRND(tape.random_seed);
15096     for (i = 0; i < num_random_calls; i++)
15097       RND(1);
15098   }
15099
15100   if (game.num_random_calls != num_random_calls)
15101   {
15102     Error(ERR_INFO, "number of random calls out of sync");
15103     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15104     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15105     Error(ERR_EXIT, "this should not happen -- please debug");
15106   }
15107 }
15108
15109 void FreeEngineSnapshotSingle()
15110 {
15111   FreeSnapshotSingle();
15112
15113   setString(&snapshot_level_identifier, NULL);
15114   snapshot_level_nr = -1;
15115 }
15116
15117 void FreeEngineSnapshotList()
15118 {
15119   FreeSnapshotList();
15120 }
15121
15122 ListNode *SaveEngineSnapshotBuffers()
15123 {
15124   ListNode *buffers = NULL;
15125
15126   /* copy some special values to a structure better suited for the snapshot */
15127
15128   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15129     SaveEngineSnapshotValues_RND();
15130   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15131     SaveEngineSnapshotValues_EM();
15132   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15133     SaveEngineSnapshotValues_SP(&buffers);
15134   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15135     SaveEngineSnapshotValues_MM(&buffers);
15136
15137   /* save values stored in special snapshot structure */
15138
15139   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15140     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15141   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15142     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15143   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15144     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15145   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15146     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15147
15148   /* save further RND engine values */
15149
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15153
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15158
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15164
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15168
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15170
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15172
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15175
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15180   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15183   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15189   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15192   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15193   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15194
15195   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15196   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15197
15198   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15199   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15200   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15201
15202   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15203   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15204
15205   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15206   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15207   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15208   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15209   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15210
15211   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15212   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15213
15214 #if 0
15215   ListNode *node = engine_snapshot_list_rnd;
15216   int num_bytes = 0;
15217
15218   while (node != NULL)
15219   {
15220     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15221
15222     node = node->next;
15223   }
15224
15225   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15226 #endif
15227
15228   return buffers;
15229 }
15230
15231 void SaveEngineSnapshotSingle()
15232 {
15233   ListNode *buffers = SaveEngineSnapshotBuffers();
15234
15235   /* finally save all snapshot buffers to single snapshot */
15236   SaveSnapshotSingle(buffers);
15237
15238   /* save level identification information */
15239   setString(&snapshot_level_identifier, leveldir_current->identifier);
15240   snapshot_level_nr = level_nr;
15241 }
15242
15243 boolean CheckSaveEngineSnapshotToList()
15244 {
15245   boolean save_snapshot =
15246     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15247      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15248       game.snapshot.changed_action) ||
15249      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15250       game.snapshot.collected_item));
15251
15252   game.snapshot.changed_action = FALSE;
15253   game.snapshot.collected_item = FALSE;
15254   game.snapshot.save_snapshot = save_snapshot;
15255
15256   return save_snapshot;
15257 }
15258
15259 void SaveEngineSnapshotToList()
15260 {
15261   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15262       tape.quick_resume)
15263     return;
15264
15265   ListNode *buffers = SaveEngineSnapshotBuffers();
15266
15267   /* finally save all snapshot buffers to snapshot list */
15268   SaveSnapshotToList(buffers);
15269 }
15270
15271 void SaveEngineSnapshotToListInitial()
15272 {
15273   FreeEngineSnapshotList();
15274
15275   SaveEngineSnapshotToList();
15276 }
15277
15278 void LoadEngineSnapshotValues()
15279 {
15280   /* restore special values from snapshot structure */
15281
15282   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15283     LoadEngineSnapshotValues_RND();
15284   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15285     LoadEngineSnapshotValues_EM();
15286   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15287     LoadEngineSnapshotValues_SP();
15288   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15289     LoadEngineSnapshotValues_MM();
15290 }
15291
15292 void LoadEngineSnapshotSingle()
15293 {
15294   LoadSnapshotSingle();
15295
15296   LoadEngineSnapshotValues();
15297 }
15298
15299 void LoadEngineSnapshot_Undo(int steps)
15300 {
15301   LoadSnapshotFromList_Older(steps);
15302
15303   LoadEngineSnapshotValues();
15304 }
15305
15306 void LoadEngineSnapshot_Redo(int steps)
15307 {
15308   LoadSnapshotFromList_Newer(steps);
15309
15310   LoadEngineSnapshotValues();
15311 }
15312
15313 boolean CheckEngineSnapshotSingle()
15314 {
15315   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15316           snapshot_level_nr == level_nr);
15317 }
15318
15319 boolean CheckEngineSnapshotList()
15320 {
15321   return CheckSnapshotList();
15322 }
15323
15324
15325 /* ---------- new game button stuff ---------------------------------------- */
15326
15327 static struct
15328 {
15329   int graphic;
15330   struct XY *pos;
15331   int gadget_id;
15332   boolean *setup_value;
15333   boolean allowed_on_tape;
15334   char *infotext;
15335 } gamebutton_info[NUM_GAME_BUTTONS] =
15336 {
15337   {
15338     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15339     GAME_CTRL_ID_STOP,                          NULL,
15340     TRUE,                                       "stop game"
15341   },
15342   {
15343     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15344     GAME_CTRL_ID_PAUSE,                         NULL,
15345     TRUE,                                       "pause game"
15346   },
15347   {
15348     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15349     GAME_CTRL_ID_PLAY,                          NULL,
15350     TRUE,                                       "play game"
15351   },
15352   {
15353     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15354     GAME_CTRL_ID_UNDO,                          NULL,
15355     TRUE,                                       "undo step"
15356   },
15357   {
15358     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15359     GAME_CTRL_ID_REDO,                          NULL,
15360     TRUE,                                       "redo step"
15361   },
15362   {
15363     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15364     GAME_CTRL_ID_SAVE,                          NULL,
15365     TRUE,                                       "save game"
15366   },
15367   {
15368     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15369     GAME_CTRL_ID_PAUSE2,                        NULL,
15370     TRUE,                                       "pause game"
15371   },
15372   {
15373     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15374     GAME_CTRL_ID_LOAD,                          NULL,
15375     TRUE,                                       "load game"
15376   },
15377   {
15378     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15379     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15380     FALSE,                                      "stop game"
15381   },
15382   {
15383     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15384     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15385     FALSE,                                      "pause game"
15386   },
15387   {
15388     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15389     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15390     FALSE,                                      "play game"
15391   },
15392   {
15393     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15394     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15395     TRUE,                                       "background music on/off"
15396   },
15397   {
15398     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15399     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15400     TRUE,                                       "sound loops on/off"
15401   },
15402   {
15403     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15404     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15405     TRUE,                                       "normal sounds on/off"
15406   },
15407   {
15408     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15409     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15410     FALSE,                                      "background music on/off"
15411   },
15412   {
15413     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15414     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15415     FALSE,                                      "sound loops on/off"
15416   },
15417   {
15418     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15419     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15420     FALSE,                                      "normal sounds on/off"
15421   }
15422 };
15423
15424 void CreateGameButtons()
15425 {
15426   int i;
15427
15428   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15429   {
15430     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15431     struct XY *pos = gamebutton_info[i].pos;
15432     struct GadgetInfo *gi;
15433     int button_type;
15434     boolean checked;
15435     unsigned int event_mask;
15436     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15437     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15438     int base_x = (on_tape ? VX : DX);
15439     int base_y = (on_tape ? VY : DY);
15440     int gd_x   = gfx->src_x;
15441     int gd_y   = gfx->src_y;
15442     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15443     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15444     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15445     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15446     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15447     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15448     int id = i;
15449
15450     if (gfx->bitmap == NULL)
15451     {
15452       game_gadget[id] = NULL;
15453
15454       continue;
15455     }
15456
15457     if (id == GAME_CTRL_ID_STOP ||
15458         id == GAME_CTRL_ID_PANEL_STOP ||
15459         id == GAME_CTRL_ID_PLAY ||
15460         id == GAME_CTRL_ID_PANEL_PLAY ||
15461         id == GAME_CTRL_ID_SAVE ||
15462         id == GAME_CTRL_ID_LOAD)
15463     {
15464       button_type = GD_TYPE_NORMAL_BUTTON;
15465       checked = FALSE;
15466       event_mask = GD_EVENT_RELEASED;
15467     }
15468     else if (id == GAME_CTRL_ID_UNDO ||
15469              id == GAME_CTRL_ID_REDO)
15470     {
15471       button_type = GD_TYPE_NORMAL_BUTTON;
15472       checked = FALSE;
15473       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15474     }
15475     else
15476     {
15477       button_type = GD_TYPE_CHECK_BUTTON;
15478       checked = (gamebutton_info[i].setup_value != NULL ?
15479                  *gamebutton_info[i].setup_value : FALSE);
15480       event_mask = GD_EVENT_PRESSED;
15481     }
15482
15483     gi = CreateGadget(GDI_CUSTOM_ID, id,
15484                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15485                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15486                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15487                       GDI_WIDTH, gfx->width,
15488                       GDI_HEIGHT, gfx->height,
15489                       GDI_TYPE, button_type,
15490                       GDI_STATE, GD_BUTTON_UNPRESSED,
15491                       GDI_CHECKED, checked,
15492                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15493                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15494                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15495                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15496                       GDI_DIRECT_DRAW, FALSE,
15497                       GDI_EVENT_MASK, event_mask,
15498                       GDI_CALLBACK_ACTION, HandleGameButtons,
15499                       GDI_END);
15500
15501     if (gi == NULL)
15502       Error(ERR_EXIT, "cannot create gadget");
15503
15504     game_gadget[id] = gi;
15505   }
15506 }
15507
15508 void FreeGameButtons()
15509 {
15510   int i;
15511
15512   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15513     FreeGadget(game_gadget[i]);
15514 }
15515
15516 static void UnmapGameButtonsAtSamePosition(int id)
15517 {
15518   int i;
15519
15520   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15521     if (i != id &&
15522         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15523         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15524       UnmapGadget(game_gadget[i]);
15525 }
15526
15527 static void UnmapGameButtonsAtSamePosition_All()
15528 {
15529   if (setup.show_snapshot_buttons)
15530   {
15531     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15532     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15533     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15534   }
15535   else
15536   {
15537     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15538     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15539     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15540
15541     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15542     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15543     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15544   }
15545 }
15546
15547 static void MapGameButtonsAtSamePosition(int id)
15548 {
15549   int i;
15550
15551   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15552     if (i != id &&
15553         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15554         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15555       MapGadget(game_gadget[i]);
15556
15557   UnmapGameButtonsAtSamePosition_All();
15558 }
15559
15560 void MapUndoRedoButtons()
15561 {
15562   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15563   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15564
15565   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15566   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15567
15568   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15569 }
15570
15571 void UnmapUndoRedoButtons()
15572 {
15573   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15574   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15575
15576   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15577   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15578
15579   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15580 }
15581
15582 void MapGameButtonsExt(boolean on_tape)
15583 {
15584   int i;
15585
15586   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15587     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15588         i != GAME_CTRL_ID_UNDO &&
15589         i != GAME_CTRL_ID_REDO)
15590       MapGadget(game_gadget[i]);
15591
15592   UnmapGameButtonsAtSamePosition_All();
15593
15594   RedrawGameButtons();
15595 }
15596
15597 void UnmapGameButtonsExt(boolean on_tape)
15598 {
15599   int i;
15600
15601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15602     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15603       UnmapGadget(game_gadget[i]);
15604 }
15605
15606 void RedrawGameButtonsExt(boolean on_tape)
15607 {
15608   int i;
15609
15610   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15611     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15612       RedrawGadget(game_gadget[i]);
15613
15614   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15615   redraw_mask &= ~REDRAW_ALL;
15616 }
15617
15618 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15619 {
15620   if (gi == NULL)
15621     return;
15622
15623   gi->checked = state;
15624 }
15625
15626 void RedrawSoundButtonGadget(int id)
15627 {
15628   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15629              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15630              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15631              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15632              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15633              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15634              id);
15635
15636   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15637   RedrawGadget(game_gadget[id2]);
15638 }
15639
15640 void MapGameButtons()
15641 {
15642   MapGameButtonsExt(FALSE);
15643 }
15644
15645 void UnmapGameButtons()
15646 {
15647   UnmapGameButtonsExt(FALSE);
15648 }
15649
15650 void RedrawGameButtons()
15651 {
15652   RedrawGameButtonsExt(FALSE);
15653 }
15654
15655 void MapGameButtonsOnTape()
15656 {
15657   MapGameButtonsExt(TRUE);
15658 }
15659
15660 void UnmapGameButtonsOnTape()
15661 {
15662   UnmapGameButtonsExt(TRUE);
15663 }
15664
15665 void RedrawGameButtonsOnTape()
15666 {
15667   RedrawGameButtonsExt(TRUE);
15668 }
15669
15670 void GameUndoRedoExt()
15671 {
15672   ClearPlayerAction();
15673
15674   tape.pausing = TRUE;
15675
15676   RedrawPlayfield();
15677   UpdateAndDisplayGameControlValues();
15678
15679   DrawCompleteVideoDisplay();
15680   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15681   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15682   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15683
15684   BackToFront();
15685 }
15686
15687 void GameUndo(int steps)
15688 {
15689   if (!CheckEngineSnapshotList())
15690     return;
15691
15692   LoadEngineSnapshot_Undo(steps);
15693
15694   GameUndoRedoExt();
15695 }
15696
15697 void GameRedo(int steps)
15698 {
15699   if (!CheckEngineSnapshotList())
15700     return;
15701
15702   LoadEngineSnapshot_Redo(steps);
15703
15704   GameUndoRedoExt();
15705 }
15706
15707 static void HandleGameButtonsExt(int id, int button)
15708 {
15709   static boolean game_undo_executed = FALSE;
15710   int steps = BUTTON_STEPSIZE(button);
15711   boolean handle_game_buttons =
15712     (game_status == GAME_MODE_PLAYING ||
15713      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15714
15715   if (!handle_game_buttons)
15716     return;
15717
15718   switch (id)
15719   {
15720     case GAME_CTRL_ID_STOP:
15721     case GAME_CTRL_ID_PANEL_STOP:
15722       if (game_status == GAME_MODE_MAIN)
15723         break;
15724
15725       if (tape.playing)
15726         TapeStop();
15727       else
15728         RequestQuitGame(TRUE);
15729
15730       break;
15731
15732     case GAME_CTRL_ID_PAUSE:
15733     case GAME_CTRL_ID_PAUSE2:
15734     case GAME_CTRL_ID_PANEL_PAUSE:
15735       if (options.network && game_status == GAME_MODE_PLAYING)
15736       {
15737 #if defined(NETWORK_AVALIABLE)
15738         if (tape.pausing)
15739           SendToServer_ContinuePlaying();
15740         else
15741           SendToServer_PausePlaying();
15742 #endif
15743       }
15744       else
15745         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15746
15747       game_undo_executed = FALSE;
15748
15749       break;
15750
15751     case GAME_CTRL_ID_PLAY:
15752     case GAME_CTRL_ID_PANEL_PLAY:
15753       if (game_status == GAME_MODE_MAIN)
15754       {
15755         StartGameActions(options.network, setup.autorecord, level.random_seed);
15756       }
15757       else if (tape.pausing)
15758       {
15759 #if defined(NETWORK_AVALIABLE)
15760         if (options.network)
15761           SendToServer_ContinuePlaying();
15762         else
15763 #endif
15764           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15765       }
15766       break;
15767
15768     case GAME_CTRL_ID_UNDO:
15769       // Important: When using "save snapshot when collecting an item" mode,
15770       // load last (current) snapshot for first "undo" after pressing "pause"
15771       // (else the last-but-one snapshot would be loaded, because the snapshot
15772       // pointer already points to the last snapshot when pressing "pause",
15773       // which is fine for "every step/move" mode, but not for "every collect")
15774       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15775           !game_undo_executed)
15776         steps--;
15777
15778       game_undo_executed = TRUE;
15779
15780       GameUndo(steps);
15781       break;
15782
15783     case GAME_CTRL_ID_REDO:
15784       GameRedo(steps);
15785       break;
15786
15787     case GAME_CTRL_ID_SAVE:
15788       TapeQuickSave();
15789       break;
15790
15791     case GAME_CTRL_ID_LOAD:
15792       TapeQuickLoad();
15793       break;
15794
15795     case SOUND_CTRL_ID_MUSIC:
15796     case SOUND_CTRL_ID_PANEL_MUSIC:
15797       if (setup.sound_music)
15798       { 
15799         setup.sound_music = FALSE;
15800
15801         FadeMusic();
15802       }
15803       else if (audio.music_available)
15804       { 
15805         setup.sound = setup.sound_music = TRUE;
15806
15807         SetAudioMode(setup.sound);
15808
15809         if (game_status == GAME_MODE_PLAYING)
15810           PlayLevelMusic();
15811       }
15812
15813       RedrawSoundButtonGadget(id);
15814
15815       break;
15816
15817     case SOUND_CTRL_ID_LOOPS:
15818     case SOUND_CTRL_ID_PANEL_LOOPS:
15819       if (setup.sound_loops)
15820         setup.sound_loops = FALSE;
15821       else if (audio.loops_available)
15822       {
15823         setup.sound = setup.sound_loops = TRUE;
15824
15825         SetAudioMode(setup.sound);
15826       }
15827
15828       RedrawSoundButtonGadget(id);
15829
15830       break;
15831
15832     case SOUND_CTRL_ID_SIMPLE:
15833     case SOUND_CTRL_ID_PANEL_SIMPLE:
15834       if (setup.sound_simple)
15835         setup.sound_simple = FALSE;
15836       else if (audio.sound_available)
15837       {
15838         setup.sound = setup.sound_simple = TRUE;
15839
15840         SetAudioMode(setup.sound);
15841       }
15842
15843       RedrawSoundButtonGadget(id);
15844
15845       break;
15846
15847     default:
15848       break;
15849   }
15850 }
15851
15852 static void HandleGameButtons(struct GadgetInfo *gi)
15853 {
15854   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15855 }
15856
15857 void HandleSoundButtonKeys(Key key)
15858 {
15859   if (key == setup.shortcut.sound_simple)
15860     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15861   else if (key == setup.shortcut.sound_loops)
15862     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15863   else if (key == setup.shortcut.sound_music)
15864     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15865 }