removed duplicate code
[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(int);
1100
1101 void TestIfGoodThingHitsBadThing(int, int, int);
1102 void TestIfBadThingHitsGoodThing(int, int, int);
1103 void TestIfPlayerTouchesBadThing(int, int);
1104 void TestIfPlayerRunsIntoBadThing(int, int, int);
1105 void TestIfBadThingTouchesPlayer(int, int);
1106 void TestIfBadThingRunsIntoPlayer(int, int, int);
1107 void TestIfFriendTouchesBadThing(int, int);
1108 void TestIfBadThingTouchesFriend(int, int);
1109 void TestIfBadThingTouchesOtherBadThing(int, int);
1110 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1111
1112 void KillPlayer(struct PlayerInfo *);
1113 void BuryPlayer(struct PlayerInfo *);
1114 void RemovePlayer(struct PlayerInfo *);
1115
1116 static int getInvisibleActiveFromInvisibleElement(int);
1117 static int getInvisibleFromInvisibleActiveElement(int);
1118
1119 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1120
1121 /* for detection of endless loops, caused by custom element programming */
1122 /* (using maximal playfield width x 10 is just a rough approximation) */
1123 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1124
1125 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1126 {                                                                       \
1127   if (recursion_loop_detected)                                          \
1128     return (rc);                                                        \
1129                                                                         \
1130   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1131   {                                                                     \
1132     recursion_loop_detected = TRUE;                                     \
1133     recursion_loop_element = (e);                                       \
1134   }                                                                     \
1135                                                                         \
1136   recursion_loop_depth++;                                               \
1137 }
1138
1139 #define RECURSION_LOOP_DETECTION_END()                                  \
1140 {                                                                       \
1141   recursion_loop_depth--;                                               \
1142 }
1143
1144 static int recursion_loop_depth;
1145 static boolean recursion_loop_detected;
1146 static boolean recursion_loop_element;
1147
1148 static int map_player_action[MAX_PLAYERS];
1149
1150
1151 /* ------------------------------------------------------------------------- */
1152 /* definition of elements that automatically change to other elements after  */
1153 /* a specified time, eventually calling a function when changing             */
1154 /* ------------------------------------------------------------------------- */
1155
1156 /* forward declaration for changer functions */
1157 static void InitBuggyBase(int, int);
1158 static void WarnBuggyBase(int, int);
1159
1160 static void InitTrap(int, int);
1161 static void ActivateTrap(int, int);
1162 static void ChangeActiveTrap(int, int);
1163
1164 static void InitRobotWheel(int, int);
1165 static void RunRobotWheel(int, int);
1166 static void StopRobotWheel(int, int);
1167
1168 static void InitTimegateWheel(int, int);
1169 static void RunTimegateWheel(int, int);
1170
1171 static void InitMagicBallDelay(int, int);
1172 static void ActivateMagicBall(int, int);
1173
1174 struct ChangingElementInfo
1175 {
1176   int element;
1177   int target_element;
1178   int change_delay;
1179   void (*pre_change_function)(int x, int y);
1180   void (*change_function)(int x, int y);
1181   void (*post_change_function)(int x, int y);
1182 };
1183
1184 static struct ChangingElementInfo change_delay_list[] =
1185 {
1186   {
1187     EL_NUT_BREAKING,
1188     EL_EMERALD,
1189     6,
1190     NULL,
1191     NULL,
1192     NULL
1193   },
1194   {
1195     EL_PEARL_BREAKING,
1196     EL_EMPTY,
1197     8,
1198     NULL,
1199     NULL,
1200     NULL
1201   },
1202   {
1203     EL_EXIT_OPENING,
1204     EL_EXIT_OPEN,
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_EXIT_CLOSING,
1212     EL_EXIT_CLOSED,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_STEEL_EXIT_OPENING,
1220     EL_STEEL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_STEEL_EXIT_CLOSING,
1228     EL_STEEL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EM_EXIT_OPENING,
1236     EL_EM_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_EM_EXIT_CLOSING,
1244     EL_EMPTY,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_STEEL_EXIT_OPENING,
1252     EL_EM_STEEL_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_STEEL_EXIT_CLOSING,
1260     EL_STEELWALL,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_SP_EXIT_OPENING,
1268     EL_SP_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_SP_EXIT_CLOSING,
1276     EL_SP_EXIT_CLOSED,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SWITCHGATE_OPENING,
1284     EL_SWITCHGATE_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SWITCHGATE_CLOSING,
1292     EL_SWITCHGATE_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_TIMEGATE_OPENING,
1300     EL_TIMEGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_TIMEGATE_CLOSING,
1308     EL_TIMEGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314
1315   {
1316     EL_ACID_SPLASH_LEFT,
1317     EL_EMPTY,
1318     8,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323   {
1324     EL_ACID_SPLASH_RIGHT,
1325     EL_EMPTY,
1326     8,
1327     NULL,
1328     NULL,
1329     NULL
1330   },
1331   {
1332     EL_SP_BUGGY_BASE,
1333     EL_SP_BUGGY_BASE_ACTIVATING,
1334     0,
1335     InitBuggyBase,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_SP_BUGGY_BASE_ACTIVATING,
1341     EL_SP_BUGGY_BASE_ACTIVE,
1342     0,
1343     InitBuggyBase,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE_ACTIVE,
1349     EL_SP_BUGGY_BASE,
1350     0,
1351     InitBuggyBase,
1352     WarnBuggyBase,
1353     NULL
1354   },
1355   {
1356     EL_TRAP,
1357     EL_TRAP_ACTIVE,
1358     0,
1359     InitTrap,
1360     NULL,
1361     ActivateTrap
1362   },
1363   {
1364     EL_TRAP_ACTIVE,
1365     EL_TRAP,
1366     31,
1367     NULL,
1368     ChangeActiveTrap,
1369     NULL
1370   },
1371   {
1372     EL_ROBOT_WHEEL_ACTIVE,
1373     EL_ROBOT_WHEEL,
1374     0,
1375     InitRobotWheel,
1376     RunRobotWheel,
1377     StopRobotWheel
1378   },
1379   {
1380     EL_TIMEGATE_SWITCH_ACTIVE,
1381     EL_TIMEGATE_SWITCH,
1382     0,
1383     InitTimegateWheel,
1384     RunTimegateWheel,
1385     NULL
1386   },
1387   {
1388     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1389     EL_DC_TIMEGATE_SWITCH,
1390     0,
1391     InitTimegateWheel,
1392     RunTimegateWheel,
1393     NULL
1394   },
1395   {
1396     EL_EMC_MAGIC_BALL_ACTIVE,
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     0,
1399     InitMagicBallDelay,
1400     NULL,
1401     ActivateMagicBall
1402   },
1403   {
1404     EL_EMC_SPRING_BUMPER_ACTIVE,
1405     EL_EMC_SPRING_BUMPER,
1406     8,
1407     NULL,
1408     NULL,
1409     NULL
1410   },
1411   {
1412     EL_DIAGONAL_SHRINKING,
1413     EL_UNDEFINED,
1414     0,
1415     NULL,
1416     NULL,
1417     NULL
1418   },
1419   {
1420     EL_DIAGONAL_GROWING,
1421     EL_UNDEFINED,
1422     0,
1423     NULL,
1424     NULL,
1425     NULL,
1426   },
1427
1428   {
1429     EL_UNDEFINED,
1430     EL_UNDEFINED,
1431     -1,
1432     NULL,
1433     NULL,
1434     NULL
1435   }
1436 };
1437
1438 struct
1439 {
1440   int element;
1441   int push_delay_fixed, push_delay_random;
1442 }
1443 push_delay_list[] =
1444 {
1445   { EL_SPRING,                  0, 0 },
1446   { EL_BALLOON,                 0, 0 },
1447
1448   { EL_SOKOBAN_OBJECT,          2, 0 },
1449   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1450   { EL_SATELLITE,               2, 0 },
1451   { EL_SP_DISK_YELLOW,          2, 0 },
1452
1453   { EL_UNDEFINED,               0, 0 },
1454 };
1455
1456 struct
1457 {
1458   int element;
1459   int move_stepsize;
1460 }
1461 move_stepsize_list[] =
1462 {
1463   { EL_AMOEBA_DROP,             2 },
1464   { EL_AMOEBA_DROPPING,         2 },
1465   { EL_QUICKSAND_FILLING,       1 },
1466   { EL_QUICKSAND_EMPTYING,      1 },
1467   { EL_QUICKSAND_FAST_FILLING,  2 },
1468   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1469   { EL_MAGIC_WALL_FILLING,      2 },
1470   { EL_MAGIC_WALL_EMPTYING,     2 },
1471   { EL_BD_MAGIC_WALL_FILLING,   2 },
1472   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1473   { EL_DC_MAGIC_WALL_FILLING,   2 },
1474   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1475
1476   { EL_UNDEFINED,               0 },
1477 };
1478
1479 struct
1480 {
1481   int element;
1482   int count;
1483 }
1484 collect_count_list[] =
1485 {
1486   { EL_EMERALD,                 1 },
1487   { EL_BD_DIAMOND,              1 },
1488   { EL_EMERALD_YELLOW,          1 },
1489   { EL_EMERALD_RED,             1 },
1490   { EL_EMERALD_PURPLE,          1 },
1491   { EL_DIAMOND,                 3 },
1492   { EL_SP_INFOTRON,             1 },
1493   { EL_PEARL,                   5 },
1494   { EL_CRYSTAL,                 8 },
1495
1496   { EL_UNDEFINED,               0 },
1497 };
1498
1499 struct
1500 {
1501   int element;
1502   int direction;
1503 }
1504 access_direction_list[] =
1505 {
1506   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1507   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1508   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1509   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1510   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1511   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1512   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1513   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1514   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1515   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1516   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1517
1518   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1519   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1520   { EL_SP_PORT_UP,                                                   MV_DOWN },
1521   { EL_SP_PORT_DOWN,                                         MV_UP           },
1522   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1523   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1524   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1525   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1526   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1527   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1528   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1529   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1530   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1531   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1532   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1533   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1534   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1535   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1536   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1537
1538   { EL_UNDEFINED,                       MV_NONE                              }
1539 };
1540
1541 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1542
1543 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1544 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1545 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1546                                  IS_JUST_CHANGING(x, y))
1547
1548 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1549
1550 /* static variables for playfield scan mode (scanning forward or backward) */
1551 static int playfield_scan_start_x = 0;
1552 static int playfield_scan_start_y = 0;
1553 static int playfield_scan_delta_x = 1;
1554 static int playfield_scan_delta_y = 1;
1555
1556 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1557                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1558                                      (y) += playfield_scan_delta_y)     \
1559                                 for ((x) = playfield_scan_start_x;      \
1560                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1561                                      (x) += playfield_scan_delta_x)
1562
1563 #ifdef DEBUG
1564 void DEBUG_SetMaximumDynamite()
1565 {
1566   int i;
1567
1568   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1569     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1570       local_player->inventory_element[local_player->inventory_size++] =
1571         EL_DYNAMITE;
1572 }
1573 #endif
1574
1575 static void InitPlayfieldScanModeVars()
1576 {
1577   if (game.use_reverse_scan_direction)
1578   {
1579     playfield_scan_start_x = lev_fieldx - 1;
1580     playfield_scan_start_y = lev_fieldy - 1;
1581
1582     playfield_scan_delta_x = -1;
1583     playfield_scan_delta_y = -1;
1584   }
1585   else
1586   {
1587     playfield_scan_start_x = 0;
1588     playfield_scan_start_y = 0;
1589
1590     playfield_scan_delta_x = 1;
1591     playfield_scan_delta_y = 1;
1592   }
1593 }
1594
1595 static void InitPlayfieldScanMode(int mode)
1596 {
1597   game.use_reverse_scan_direction =
1598     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1599
1600   InitPlayfieldScanModeVars();
1601 }
1602
1603 static int get_move_delay_from_stepsize(int move_stepsize)
1604 {
1605   move_stepsize =
1606     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1607
1608   /* make sure that stepsize value is always a power of 2 */
1609   move_stepsize = (1 << log_2(move_stepsize));
1610
1611   return TILEX / move_stepsize;
1612 }
1613
1614 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1615                                boolean init_game)
1616 {
1617   int player_nr = player->index_nr;
1618   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1619   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1620
1621   /* do no immediately change move delay -- the player might just be moving */
1622   player->move_delay_value_next = move_delay;
1623
1624   /* information if player can move must be set separately */
1625   player->cannot_move = cannot_move;
1626
1627   if (init_game)
1628   {
1629     player->move_delay       = game.initial_move_delay[player_nr];
1630     player->move_delay_value = game.initial_move_delay_value[player_nr];
1631
1632     player->move_delay_value_next = -1;
1633
1634     player->move_delay_reset_counter = 0;
1635   }
1636 }
1637
1638 void GetPlayerConfig()
1639 {
1640   GameFrameDelay = setup.game_frame_delay;
1641
1642   if (!audio.sound_available)
1643     setup.sound_simple = FALSE;
1644
1645   if (!audio.loops_available)
1646     setup.sound_loops = FALSE;
1647
1648   if (!audio.music_available)
1649     setup.sound_music = FALSE;
1650
1651   if (!video.fullscreen_available)
1652     setup.fullscreen = FALSE;
1653
1654   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1655
1656   SetAudioMode(setup.sound);
1657 }
1658
1659 int GetElementFromGroupElement(int element)
1660 {
1661   if (IS_GROUP_ELEMENT(element))
1662   {
1663     struct ElementGroupInfo *group = element_info[element].group;
1664     int last_anim_random_frame = gfx.anim_random_frame;
1665     int element_pos;
1666
1667     if (group->choice_mode == ANIM_RANDOM)
1668       gfx.anim_random_frame = RND(group->num_elements_resolved);
1669
1670     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1671                                     group->choice_mode, 0,
1672                                     group->choice_pos);
1673
1674     if (group->choice_mode == ANIM_RANDOM)
1675       gfx.anim_random_frame = last_anim_random_frame;
1676
1677     group->choice_pos++;
1678
1679     element = group->element_resolved[element_pos];
1680   }
1681
1682   return element;
1683 }
1684
1685 static void InitPlayerField(int x, int y, int element, boolean init_game)
1686 {
1687   if (element == EL_SP_MURPHY)
1688   {
1689     if (init_game)
1690     {
1691       if (stored_player[0].present)
1692       {
1693         Feld[x][y] = EL_SP_MURPHY_CLONE;
1694
1695         return;
1696       }
1697       else
1698       {
1699         stored_player[0].initial_element = element;
1700         stored_player[0].use_murphy = TRUE;
1701
1702         if (!level.use_artwork_element[0])
1703           stored_player[0].artwork_element = EL_SP_MURPHY;
1704       }
1705
1706       Feld[x][y] = EL_PLAYER_1;
1707     }
1708   }
1709
1710   if (init_game)
1711   {
1712     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1713     int jx = player->jx, jy = player->jy;
1714
1715     player->present = TRUE;
1716
1717     player->block_last_field = (element == EL_SP_MURPHY ?
1718                                 level.sp_block_last_field :
1719                                 level.block_last_field);
1720
1721     /* ---------- initialize player's last field block delay --------------- */
1722
1723     /* always start with reliable default value (no adjustment needed) */
1724     player->block_delay_adjustment = 0;
1725
1726     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1727     if (player->block_last_field && element == EL_SP_MURPHY)
1728       player->block_delay_adjustment = 1;
1729
1730     /* special case 2: in game engines before 3.1.1, blocking was different */
1731     if (game.use_block_last_field_bug)
1732       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1733
1734     if (!network.enabled || player->connected_network)
1735     {
1736       player->active = TRUE;
1737
1738       /* remove potentially duplicate players */
1739       if (StorePlayer[jx][jy] == Feld[x][y])
1740         StorePlayer[jx][jy] = 0;
1741
1742       StorePlayer[x][y] = Feld[x][y];
1743
1744 #if DEBUG_INIT_PLAYER
1745       if (options.debug)
1746       {
1747         printf("- player element %d activated", player->element_nr);
1748         printf(" (local player is %d and currently %s)\n",
1749                local_player->element_nr,
1750                local_player->active ? "active" : "not active");
1751       }
1752     }
1753 #endif
1754
1755     Feld[x][y] = EL_EMPTY;
1756
1757     player->jx = player->last_jx = x;
1758     player->jy = player->last_jy = y;
1759   }
1760
1761   if (!init_game)
1762   {
1763     int player_nr = GET_PLAYER_NR(element);
1764     struct PlayerInfo *player = &stored_player[player_nr];
1765
1766     if (player->active && player->killed)
1767       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1768   }
1769 }
1770
1771 static void InitField(int x, int y, boolean init_game)
1772 {
1773   int element = Feld[x][y];
1774
1775   switch (element)
1776   {
1777     case EL_SP_MURPHY:
1778     case EL_PLAYER_1:
1779     case EL_PLAYER_2:
1780     case EL_PLAYER_3:
1781     case EL_PLAYER_4:
1782       InitPlayerField(x, y, element, init_game);
1783       break;
1784
1785     case EL_SOKOBAN_FIELD_PLAYER:
1786       element = Feld[x][y] = EL_PLAYER_1;
1787       InitField(x, y, init_game);
1788
1789       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1790       InitField(x, y, init_game);
1791       break;
1792
1793     case EL_SOKOBAN_FIELD_EMPTY:
1794       local_player->sokobanfields_still_needed++;
1795       break;
1796
1797     case EL_STONEBLOCK:
1798       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1800       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1801         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1802       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1803         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1804       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1805         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1806       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1807         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1808       break;
1809
1810     case EL_BUG:
1811     case EL_BUG_RIGHT:
1812     case EL_BUG_UP:
1813     case EL_BUG_LEFT:
1814     case EL_BUG_DOWN:
1815     case EL_SPACESHIP:
1816     case EL_SPACESHIP_RIGHT:
1817     case EL_SPACESHIP_UP:
1818     case EL_SPACESHIP_LEFT:
1819     case EL_SPACESHIP_DOWN:
1820     case EL_BD_BUTTERFLY:
1821     case EL_BD_BUTTERFLY_RIGHT:
1822     case EL_BD_BUTTERFLY_UP:
1823     case EL_BD_BUTTERFLY_LEFT:
1824     case EL_BD_BUTTERFLY_DOWN:
1825     case EL_BD_FIREFLY:
1826     case EL_BD_FIREFLY_RIGHT:
1827     case EL_BD_FIREFLY_UP:
1828     case EL_BD_FIREFLY_LEFT:
1829     case EL_BD_FIREFLY_DOWN:
1830     case EL_PACMAN_RIGHT:
1831     case EL_PACMAN_UP:
1832     case EL_PACMAN_LEFT:
1833     case EL_PACMAN_DOWN:
1834     case EL_YAMYAM:
1835     case EL_YAMYAM_LEFT:
1836     case EL_YAMYAM_RIGHT:
1837     case EL_YAMYAM_UP:
1838     case EL_YAMYAM_DOWN:
1839     case EL_DARK_YAMYAM:
1840     case EL_ROBOT:
1841     case EL_PACMAN:
1842     case EL_SP_SNIKSNAK:
1843     case EL_SP_ELECTRON:
1844     case EL_MOLE:
1845     case EL_MOLE_LEFT:
1846     case EL_MOLE_RIGHT:
1847     case EL_MOLE_UP:
1848     case EL_MOLE_DOWN:
1849       InitMovDir(x, y);
1850       break;
1851
1852     case EL_AMOEBA_FULL:
1853     case EL_BD_AMOEBA:
1854       InitAmoebaNr(x, y);
1855       break;
1856
1857     case EL_AMOEBA_DROP:
1858       if (y == lev_fieldy - 1)
1859       {
1860         Feld[x][y] = EL_AMOEBA_GROWING;
1861         Store[x][y] = EL_AMOEBA_WET;
1862       }
1863       break;
1864
1865     case EL_DYNAMITE_ACTIVE:
1866     case EL_SP_DISK_RED_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1871       MovDelay[x][y] = 96;
1872       break;
1873
1874     case EL_EM_DYNAMITE_ACTIVE:
1875       MovDelay[x][y] = 32;
1876       break;
1877
1878     case EL_LAMP:
1879       local_player->lights_still_needed++;
1880       break;
1881
1882     case EL_PENGUIN:
1883       local_player->friends_still_needed++;
1884       break;
1885
1886     case EL_PIG:
1887     case EL_DRAGON:
1888       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1889       break;
1890
1891     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1899     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1902     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1903       if (init_game)
1904       {
1905         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1906         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1908
1909         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1910         {
1911           game.belt_dir[belt_nr] = belt_dir;
1912           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1913         }
1914         else    /* more than one switch -- set it like the first switch */
1915         {
1916           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1917         }
1918       }
1919       break;
1920
1921     case EL_LIGHT_SWITCH_ACTIVE:
1922       if (init_game)
1923         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1924       break;
1925
1926     case EL_INVISIBLE_STEELWALL:
1927     case EL_INVISIBLE_WALL:
1928     case EL_INVISIBLE_SAND:
1929       if (game.light_time_left > 0 ||
1930           game.lenses_time_left > 0)
1931         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1932       break;
1933
1934     case EL_EMC_MAGIC_BALL:
1935       if (game.ball_state)
1936         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1937       break;
1938
1939     case EL_EMC_MAGIC_BALL_SWITCH:
1940       if (game.ball_state)
1941         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1942       break;
1943
1944     case EL_TRIGGER_PLAYER:
1945     case EL_TRIGGER_ELEMENT:
1946     case EL_TRIGGER_CE_VALUE:
1947     case EL_TRIGGER_CE_SCORE:
1948     case EL_SELF:
1949     case EL_ANY_ELEMENT:
1950     case EL_CURRENT_CE_VALUE:
1951     case EL_CURRENT_CE_SCORE:
1952     case EL_PREV_CE_1:
1953     case EL_PREV_CE_2:
1954     case EL_PREV_CE_3:
1955     case EL_PREV_CE_4:
1956     case EL_PREV_CE_5:
1957     case EL_PREV_CE_6:
1958     case EL_PREV_CE_7:
1959     case EL_PREV_CE_8:
1960     case EL_NEXT_CE_1:
1961     case EL_NEXT_CE_2:
1962     case EL_NEXT_CE_3:
1963     case EL_NEXT_CE_4:
1964     case EL_NEXT_CE_5:
1965     case EL_NEXT_CE_6:
1966     case EL_NEXT_CE_7:
1967     case EL_NEXT_CE_8:
1968       /* reference elements should not be used on the playfield */
1969       Feld[x][y] = EL_EMPTY;
1970       break;
1971
1972     default:
1973       if (IS_CUSTOM_ELEMENT(element))
1974       {
1975         if (CAN_MOVE(element))
1976           InitMovDir(x, y);
1977
1978         if (!element_info[element].use_last_ce_value || init_game)
1979           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1980       }
1981       else if (IS_GROUP_ELEMENT(element))
1982       {
1983         Feld[x][y] = GetElementFromGroupElement(element);
1984
1985         InitField(x, y, init_game);
1986       }
1987
1988       break;
1989   }
1990
1991   if (!init_game)
1992     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1993 }
1994
1995 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1996 {
1997   InitField(x, y, init_game);
1998
1999   /* not needed to call InitMovDir() -- already done by InitField()! */
2000   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2001       CAN_MOVE(Feld[x][y]))
2002     InitMovDir(x, y);
2003 }
2004
2005 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2006 {
2007   int old_element = Feld[x][y];
2008
2009   InitField(x, y, init_game);
2010
2011   /* not needed to call InitMovDir() -- already done by InitField()! */
2012   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2013       CAN_MOVE(old_element) &&
2014       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2015     InitMovDir(x, y);
2016
2017   /* this case is in fact a combination of not less than three bugs:
2018      first, it calls InitMovDir() for elements that can move, although this is
2019      already done by InitField(); then, it checks the element that was at this
2020      field _before_ the call to InitField() (which can change it); lastly, it
2021      was not called for "mole with direction" elements, which were treated as
2022      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2023   */
2024 }
2025
2026 static int get_key_element_from_nr(int key_nr)
2027 {
2028   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2029                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2030                           EL_EM_KEY_1 : EL_KEY_1);
2031
2032   return key_base_element + key_nr;
2033 }
2034
2035 static int get_next_dropped_element(struct PlayerInfo *player)
2036 {
2037   return (player->inventory_size > 0 ?
2038           player->inventory_element[player->inventory_size - 1] :
2039           player->inventory_infinite_element != EL_UNDEFINED ?
2040           player->inventory_infinite_element :
2041           player->dynabombs_left > 0 ?
2042           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2043           EL_UNDEFINED);
2044 }
2045
2046 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2047 {
2048   /* pos >= 0: get element from bottom of the stack;
2049      pos <  0: get element from top of the stack */
2050
2051   if (pos < 0)
2052   {
2053     int min_inventory_size = -pos;
2054     int inventory_pos = player->inventory_size - min_inventory_size;
2055     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2056
2057     return (player->inventory_size >= min_inventory_size ?
2058             player->inventory_element[inventory_pos] :
2059             player->inventory_infinite_element != EL_UNDEFINED ?
2060             player->inventory_infinite_element :
2061             player->dynabombs_left >= min_dynabombs_left ?
2062             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2063             EL_UNDEFINED);
2064   }
2065   else
2066   {
2067     int min_dynabombs_left = pos + 1;
2068     int min_inventory_size = pos + 1 - player->dynabombs_left;
2069     int inventory_pos = pos - player->dynabombs_left;
2070
2071     return (player->inventory_infinite_element != EL_UNDEFINED ?
2072             player->inventory_infinite_element :
2073             player->dynabombs_left >= min_dynabombs_left ?
2074             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2075             player->inventory_size >= min_inventory_size ?
2076             player->inventory_element[inventory_pos] :
2077             EL_UNDEFINED);
2078   }
2079 }
2080
2081 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2082 {
2083   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2084   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2085   int compare_result;
2086
2087   if (gpo1->sort_priority != gpo2->sort_priority)
2088     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2089   else
2090     compare_result = gpo1->nr - gpo2->nr;
2091
2092   return compare_result;
2093 }
2094
2095 int getPlayerInventorySize(int player_nr)
2096 {
2097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2098     return level.native_em_level->ply[player_nr]->dynamite;
2099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2100     return level.native_sp_level->game_sp->red_disk_count;
2101   else
2102     return stored_player[player_nr].inventory_size;
2103 }
2104
2105 void InitGameControlValues()
2106 {
2107   int i;
2108
2109   for (i = 0; game_panel_controls[i].nr != -1; i++)
2110   {
2111     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2112     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2113     struct TextPosInfo *pos = gpc->pos;
2114     int nr = gpc->nr;
2115     int type = gpc->type;
2116
2117     if (nr != i)
2118     {
2119       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2120       Error(ERR_EXIT, "this should not happen -- please debug");
2121     }
2122
2123     /* force update of game controls after initialization */
2124     gpc->value = gpc->last_value = -1;
2125     gpc->frame = gpc->last_frame = -1;
2126     gpc->gfx_frame = -1;
2127
2128     /* determine panel value width for later calculation of alignment */
2129     if (type == TYPE_INTEGER || type == TYPE_STRING)
2130     {
2131       pos->width = pos->size * getFontWidth(pos->font);
2132       pos->height = getFontHeight(pos->font);
2133     }
2134     else if (type == TYPE_ELEMENT)
2135     {
2136       pos->width = pos->size;
2137       pos->height = pos->size;
2138     }
2139
2140     /* fill structure for game panel draw order */
2141     gpo->nr = gpc->nr;
2142     gpo->sort_priority = pos->sort_priority;
2143   }
2144
2145   /* sort game panel controls according to sort_priority and control number */
2146   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2147         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2148 }
2149
2150 void UpdatePlayfieldElementCount()
2151 {
2152   boolean use_element_count = FALSE;
2153   int i, j, x, y;
2154
2155   /* first check if it is needed at all to calculate playfield element count */
2156   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2157     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2158       use_element_count = TRUE;
2159
2160   if (!use_element_count)
2161     return;
2162
2163   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2164     element_info[i].element_count = 0;
2165
2166   SCAN_PLAYFIELD(x, y)
2167   {
2168     element_info[Feld[x][y]].element_count++;
2169   }
2170
2171   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2172     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2173       if (IS_IN_GROUP(j, i))
2174         element_info[EL_GROUP_START + i].element_count +=
2175           element_info[j].element_count;
2176 }
2177
2178 void UpdateGameControlValues()
2179 {
2180   int i, k;
2181   int time = (local_player->LevelSolved ?
2182               local_player->LevelSolved_CountingTime :
2183               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2184               level.native_em_level->lev->time :
2185               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2186               level.native_sp_level->game_sp->time_played :
2187               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2188               game_mm.energy_left :
2189               game.no_time_limit ? TimePlayed : TimeLeft);
2190   int score = (local_player->LevelSolved ?
2191                local_player->LevelSolved_CountingScore :
2192                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2193                level.native_em_level->lev->score :
2194                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2195                level.native_sp_level->game_sp->score :
2196                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2197                game_mm.score :
2198                local_player->score);
2199   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2200               level.native_em_level->lev->required :
2201               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2202               level.native_sp_level->game_sp->infotrons_still_needed :
2203               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2204               game_mm.kettles_still_needed :
2205               local_player->gems_still_needed);
2206   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2207                      level.native_em_level->lev->required > 0 :
2208                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2209                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2210                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2211                      game_mm.kettles_still_needed > 0 ||
2212                      game_mm.lights_still_needed > 0 :
2213                      local_player->gems_still_needed > 0 ||
2214                      local_player->sokobanfields_still_needed > 0 ||
2215                      local_player->lights_still_needed > 0);
2216   int health = (local_player->LevelSolved ?
2217                 local_player->LevelSolved_CountingHealth :
2218                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2219                 MM_HEALTH(game_mm.laser_overload_value) :
2220                 local_player->health);
2221
2222   UpdatePlayfieldElementCount();
2223
2224   /* update game panel control values */
2225
2226   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2227   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2228
2229   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2230   for (i = 0; i < MAX_NUM_KEYS; i++)
2231     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2232   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2234
2235   if (game.centered_player_nr == -1)
2236   {
2237     for (i = 0; i < MAX_PLAYERS; i++)
2238     {
2239       /* only one player in Supaplex game engine */
2240       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2241         break;
2242
2243       for (k = 0; k < MAX_NUM_KEYS; k++)
2244       {
2245         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246         {
2247           if (level.native_em_level->ply[i]->keys & (1 << k))
2248             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2249               get_key_element_from_nr(k);
2250         }
2251         else if (stored_player[i].key[k])
2252           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2253             get_key_element_from_nr(k);
2254       }
2255
2256       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2257         getPlayerInventorySize(i);
2258
2259       if (stored_player[i].num_white_keys > 0)
2260         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2261           EL_DC_KEY_WHITE;
2262
2263       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2264         stored_player[i].num_white_keys;
2265     }
2266   }
2267   else
2268   {
2269     int player_nr = game.centered_player_nr;
2270
2271     for (k = 0; k < MAX_NUM_KEYS; k++)
2272     {
2273       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2274       {
2275         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2276           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277             get_key_element_from_nr(k);
2278       }
2279       else if (stored_player[player_nr].key[k])
2280         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2281           get_key_element_from_nr(k);
2282     }
2283
2284     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285       getPlayerInventorySize(player_nr);
2286
2287     if (stored_player[player_nr].num_white_keys > 0)
2288       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2289
2290     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2291       stored_player[player_nr].num_white_keys;
2292   }
2293
2294   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2295   {
2296     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2297       get_inventory_element_from_pos(local_player, i);
2298     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2299       get_inventory_element_from_pos(local_player, -i - 1);
2300   }
2301
2302   game_panel_controls[GAME_PANEL_SCORE].value = score;
2303   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2304
2305   game_panel_controls[GAME_PANEL_TIME].value = time;
2306
2307   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2308   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2309   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2310
2311   if (level.time == 0)
2312     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2313   else
2314     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2315
2316   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2317   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2318
2319   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2320
2321   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2322     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2325     local_player->shield_normal_time_left;
2326   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2327     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2328      EL_EMPTY);
2329   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2330     local_player->shield_deadly_time_left;
2331
2332   game_panel_controls[GAME_PANEL_EXIT].value =
2333     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2334
2335   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2336     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2337   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2338     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2339      EL_EMC_MAGIC_BALL_SWITCH);
2340
2341   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2342     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2343   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2344     game.light_time_left;
2345
2346   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2347     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2348   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2349     game.timegate_time_left;
2350
2351   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2352     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2353
2354   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2355     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2357     game.lenses_time_left;
2358
2359   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2360     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2362     game.magnify_time_left;
2363
2364   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2365     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2366      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2367      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2368      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2369      EL_BALLOON_SWITCH_NONE);
2370
2371   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2372     local_player->dynabomb_count;
2373   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2374     local_player->dynabomb_size;
2375   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2376     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2377
2378   game_panel_controls[GAME_PANEL_PENGUINS].value =
2379     local_player->friends_still_needed;
2380
2381   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2382     local_player->sokobanfields_still_needed;
2383   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2384     local_player->sokobanfields_still_needed;
2385
2386   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2387     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2388
2389   for (i = 0; i < NUM_BELTS; i++)
2390   {
2391     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2392       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2393        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2394     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2395       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2396   }
2397
2398   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2399     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2400   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2401     game.magic_wall_time_left;
2402
2403   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2404     local_player->gravity;
2405
2406   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2407     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2408
2409   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2410     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2411       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2412        game.panel.element[i].id : EL_UNDEFINED);
2413
2414   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2415     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2416       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2417        element_info[game.panel.element_count[i].id].element_count : 0);
2418
2419   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2420     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2421       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2422        element_info[game.panel.ce_score[i].id].collect_score : 0);
2423
2424   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2425     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2426       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2427        element_info[game.panel.ce_score_element[i].id].collect_score :
2428        EL_UNDEFINED);
2429
2430   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2433
2434   /* update game panel control frames */
2435
2436   for (i = 0; game_panel_controls[i].nr != -1; i++)
2437   {
2438     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2439
2440     if (gpc->type == TYPE_ELEMENT)
2441     {
2442       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2443       {
2444         int last_anim_random_frame = gfx.anim_random_frame;
2445         int element = gpc->value;
2446         int graphic = el2panelimg(element);
2447
2448         if (gpc->value != gpc->last_value)
2449         {
2450           gpc->gfx_frame = 0;
2451           gpc->gfx_random = INIT_GFX_RANDOM();
2452         }
2453         else
2454         {
2455           gpc->gfx_frame++;
2456
2457           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2458               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2459             gpc->gfx_random = INIT_GFX_RANDOM();
2460         }
2461
2462         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2463           gfx.anim_random_frame = gpc->gfx_random;
2464
2465         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2466           gpc->gfx_frame = element_info[element].collect_score;
2467
2468         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2469                                               gpc->gfx_frame);
2470
2471         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2472           gfx.anim_random_frame = last_anim_random_frame;
2473       }
2474     }
2475     else if (gpc->type == TYPE_GRAPHIC)
2476     {
2477       if (gpc->graphic != IMG_UNDEFINED)
2478       {
2479         int last_anim_random_frame = gfx.anim_random_frame;
2480         int graphic = gpc->graphic;
2481
2482         if (gpc->value != gpc->last_value)
2483         {
2484           gpc->gfx_frame = 0;
2485           gpc->gfx_random = INIT_GFX_RANDOM();
2486         }
2487         else
2488         {
2489           gpc->gfx_frame++;
2490
2491           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2492               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2493             gpc->gfx_random = INIT_GFX_RANDOM();
2494         }
2495
2496         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2497           gfx.anim_random_frame = gpc->gfx_random;
2498
2499         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2500
2501         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2502           gfx.anim_random_frame = last_anim_random_frame;
2503       }
2504     }
2505   }
2506 }
2507
2508 void DisplayGameControlValues()
2509 {
2510   boolean redraw_panel = FALSE;
2511   int i;
2512
2513   for (i = 0; game_panel_controls[i].nr != -1; i++)
2514   {
2515     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2516
2517     if (PANEL_DEACTIVATED(gpc->pos))
2518       continue;
2519
2520     if (gpc->value == gpc->last_value &&
2521         gpc->frame == gpc->last_frame)
2522       continue;
2523
2524     redraw_panel = TRUE;
2525   }
2526
2527   if (!redraw_panel)
2528     return;
2529
2530   /* copy default game door content to main double buffer */
2531
2532   /* !!! CHECK AGAIN !!! */
2533   SetPanelBackground();
2534   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2535   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2536
2537   /* redraw game control buttons */
2538   RedrawGameButtons();
2539
2540   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2541
2542   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2543   {
2544     int nr = game_panel_order[i].nr;
2545     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2546     struct TextPosInfo *pos = gpc->pos;
2547     int type = gpc->type;
2548     int value = gpc->value;
2549     int frame = gpc->frame;
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558     gpc->last_value = value;
2559     gpc->last_frame = frame;
2560
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564           nr == GAME_PANEL_TIME)
2565       {
2566         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567
2568         if (use_dynamic_size)           /* use dynamic number of digits */
2569         {
2570           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572           int size2 = size1 + 1;
2573           int font1 = pos->font;
2574           int font2 = pos->font_alt;
2575
2576           size = (value < value_change ? size1 : size2);
2577           font = (value < value_change ? font1 : font2);
2578         }
2579       }
2580
2581       /* correct text size if "digits" is zero or less */
2582       if (size <= 0)
2583         size = strlen(int2str(value, size));
2584
2585       /* dynamically correct text alignment */
2586       pos->width = size * getFontWidth(font);
2587
2588       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2589                   int2str(value, size), font, mask_mode);
2590     }
2591     else if (type == TYPE_ELEMENT)
2592     {
2593       int element, graphic;
2594       Bitmap *src_bitmap;
2595       int src_x, src_y;
2596       int width, height;
2597       int dst_x = PANEL_XPOS(pos);
2598       int dst_y = PANEL_YPOS(pos);
2599
2600       if (value != EL_UNDEFINED && value != EL_EMPTY)
2601       {
2602         element = value;
2603         graphic = el2panelimg(value);
2604
2605         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2606
2607         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2608           size = TILESIZE;
2609
2610         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2611                               &src_x, &src_y);
2612
2613         width  = graphic_info[graphic].width  * size / TILESIZE;
2614         height = graphic_info[graphic].height * size / TILESIZE;
2615
2616         if (draw_masked)
2617           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2618                            dst_x, dst_y);
2619         else
2620           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2621                      dst_x, dst_y);
2622       }
2623     }
2624     else if (type == TYPE_GRAPHIC)
2625     {
2626       int graphic        = gpc->graphic;
2627       int graphic_active = gpc->graphic_active;
2628       Bitmap *src_bitmap;
2629       int src_x, src_y;
2630       int width, height;
2631       int dst_x = PANEL_XPOS(pos);
2632       int dst_y = PANEL_YPOS(pos);
2633       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2634                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2635
2636       if (graphic != IMG_UNDEFINED && !skip)
2637       {
2638         if (pos->style == STYLE_REVERSE)
2639           value = 100 - value;
2640
2641         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2642
2643         if (pos->direction & MV_HORIZONTAL)
2644         {
2645           width  = graphic_info[graphic_active].width * value / 100;
2646           height = graphic_info[graphic_active].height;
2647
2648           if (pos->direction == MV_LEFT)
2649           {
2650             src_x += graphic_info[graphic_active].width - width;
2651             dst_x += graphic_info[graphic_active].width - width;
2652           }
2653         }
2654         else
2655         {
2656           width  = graphic_info[graphic_active].width;
2657           height = graphic_info[graphic_active].height * value / 100;
2658
2659           if (pos->direction == MV_UP)
2660           {
2661             src_y += graphic_info[graphic_active].height - height;
2662             dst_y += graphic_info[graphic_active].height - height;
2663           }
2664         }
2665
2666         if (draw_masked)
2667           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2668                            dst_x, dst_y);
2669         else
2670           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2671                      dst_x, dst_y);
2672
2673         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2674
2675         if (pos->direction & MV_HORIZONTAL)
2676         {
2677           if (pos->direction == MV_RIGHT)
2678           {
2679             src_x += width;
2680             dst_x += width;
2681           }
2682           else
2683           {
2684             dst_x = PANEL_XPOS(pos);
2685           }
2686
2687           width = graphic_info[graphic].width - width;
2688         }
2689         else
2690         {
2691           if (pos->direction == MV_DOWN)
2692           {
2693             src_y += height;
2694             dst_y += height;
2695           }
2696           else
2697           {
2698             dst_y = PANEL_YPOS(pos);
2699           }
2700
2701           height = graphic_info[graphic].height - height;
2702         }
2703
2704         if (draw_masked)
2705           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2706                            dst_x, dst_y);
2707         else
2708           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2709                      dst_x, dst_y);
2710       }
2711     }
2712     else if (type == TYPE_STRING)
2713     {
2714       boolean active = (value != 0);
2715       char *state_normal = "off";
2716       char *state_active = "on";
2717       char *state = (active ? state_active : state_normal);
2718       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2719                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2720                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2721                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2722
2723       if (nr == GAME_PANEL_GRAVITY_STATE)
2724       {
2725         int font1 = pos->font;          /* (used for normal state) */
2726         int font2 = pos->font_alt;      /* (used for active state) */
2727
2728         font = (active ? font2 : font1);
2729       }
2730
2731       if (s != NULL)
2732       {
2733         char *s_cut;
2734
2735         if (size <= 0)
2736         {
2737           /* don't truncate output if "chars" is zero or less */
2738           size = strlen(s);
2739
2740           /* dynamically correct text alignment */
2741           pos->width = size * getFontWidth(font);
2742         }
2743
2744         s_cut = getStringCopyN(s, size);
2745
2746         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2747                     s_cut, font, mask_mode);
2748
2749         free(s_cut);
2750       }
2751     }
2752
2753     redraw_mask |= REDRAW_DOOR_1;
2754   }
2755
2756   SetGameStatus(GAME_MODE_PLAYING);
2757 }
2758
2759 void UpdateAndDisplayGameControlValues()
2760 {
2761   if (tape.deactivate_display)
2762     return;
2763
2764   UpdateGameControlValues();
2765   DisplayGameControlValues();
2766 }
2767
2768 void UpdateGameDoorValues()
2769 {
2770   UpdateGameControlValues();
2771 }
2772
2773 void DrawGameDoorValues()
2774 {
2775   DisplayGameControlValues();
2776 }
2777
2778
2779 /*
2780   =============================================================================
2781   InitGameEngine()
2782   -----------------------------------------------------------------------------
2783   initialize game engine due to level / tape version number
2784   =============================================================================
2785 */
2786
2787 static void InitGameEngine()
2788 {
2789   int i, j, k, l, x, y;
2790
2791   /* set game engine from tape file when re-playing, else from level file */
2792   game.engine_version = (tape.playing ? tape.engine_version :
2793                          level.game_version);
2794
2795   /* set single or multi-player game mode (needed for re-playing tapes) */
2796   game.team_mode = setup.team_mode;
2797
2798   if (tape.playing)
2799   {
2800     int num_players = 0;
2801
2802     for (i = 0; i < MAX_PLAYERS; i++)
2803       if (tape.player_participates[i])
2804         num_players++;
2805
2806     /* multi-player tapes contain input data for more than one player */
2807     game.team_mode = (num_players > 1);
2808   }
2809
2810   /* ---------------------------------------------------------------------- */
2811   /* set flags for bugs and changes according to active game engine version */
2812   /* ---------------------------------------------------------------------- */
2813
2814   /*
2815     Summary of bugfix/change:
2816     Fixed handling for custom elements that change when pushed by the player.
2817
2818     Fixed/changed in version:
2819     3.1.0
2820
2821     Description:
2822     Before 3.1.0, custom elements that "change when pushing" changed directly
2823     after the player started pushing them (until then handled in "DigField()").
2824     Since 3.1.0, these custom elements are not changed until the "pushing"
2825     move of the element is finished (now handled in "ContinueMoving()").
2826
2827     Affected levels/tapes:
2828     The first condition is generally needed for all levels/tapes before version
2829     3.1.0, which might use the old behaviour before it was changed; known tapes
2830     that are affected are some tapes from the level set "Walpurgis Gardens" by
2831     Jamie Cullen.
2832     The second condition is an exception from the above case and is needed for
2833     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2834     above (including some development versions of 3.1.0), but before it was
2835     known that this change would break tapes like the above and was fixed in
2836     3.1.1, so that the changed behaviour was active although the engine version
2837     while recording maybe was before 3.1.0. There is at least one tape that is
2838     affected by this exception, which is the tape for the one-level set "Bug
2839     Machine" by Juergen Bonhagen.
2840   */
2841
2842   game.use_change_when_pushing_bug =
2843     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2844      !(tape.playing &&
2845        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2846        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2847
2848   /*
2849     Summary of bugfix/change:
2850     Fixed handling for blocking the field the player leaves when moving.
2851
2852     Fixed/changed in version:
2853     3.1.1
2854
2855     Description:
2856     Before 3.1.1, when "block last field when moving" was enabled, the field
2857     the player is leaving when moving was blocked for the time of the move,
2858     and was directly unblocked afterwards. This resulted in the last field
2859     being blocked for exactly one less than the number of frames of one player
2860     move. Additionally, even when blocking was disabled, the last field was
2861     blocked for exactly one frame.
2862     Since 3.1.1, due to changes in player movement handling, the last field
2863     is not blocked at all when blocking is disabled. When blocking is enabled,
2864     the last field is blocked for exactly the number of frames of one player
2865     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2866     last field is blocked for exactly one more than the number of frames of
2867     one player move.
2868
2869     Affected levels/tapes:
2870     (!!! yet to be determined -- probably many !!!)
2871   */
2872
2873   game.use_block_last_field_bug =
2874     (game.engine_version < VERSION_IDENT(3,1,1,0));
2875
2876   game_em.use_single_button =
2877     (game.engine_version > VERSION_IDENT(4,0,0,2));
2878
2879   game_em.use_snap_key_bug =
2880     (game.engine_version < VERSION_IDENT(4,0,1,0));
2881
2882   /* ---------------------------------------------------------------------- */
2883
2884   /* set maximal allowed number of custom element changes per game frame */
2885   game.max_num_changes_per_frame = 1;
2886
2887   /* default scan direction: scan playfield from top/left to bottom/right */
2888   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2889
2890   /* dynamically adjust element properties according to game engine version */
2891   InitElementPropertiesEngine(game.engine_version);
2892
2893 #if 0
2894   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2895   printf("          tape version == %06d [%s] [file: %06d]\n",
2896          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2897          tape.file_version);
2898   printf("       => game.engine_version == %06d\n", game.engine_version);
2899 #endif
2900
2901   /* ---------- initialize player's initial move delay --------------------- */
2902
2903   /* dynamically adjust player properties according to level information */
2904   for (i = 0; i < MAX_PLAYERS; i++)
2905     game.initial_move_delay_value[i] =
2906       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2907
2908   /* dynamically adjust player properties according to game engine version */
2909   for (i = 0; i < MAX_PLAYERS; i++)
2910     game.initial_move_delay[i] =
2911       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2912        game.initial_move_delay_value[i] : 0);
2913
2914   /* ---------- initialize player's initial push delay --------------------- */
2915
2916   /* dynamically adjust player properties according to game engine version */
2917   game.initial_push_delay_value =
2918     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2919
2920   /* ---------- initialize changing elements ------------------------------- */
2921
2922   /* initialize changing elements information */
2923   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2924   {
2925     struct ElementInfo *ei = &element_info[i];
2926
2927     /* this pointer might have been changed in the level editor */
2928     ei->change = &ei->change_page[0];
2929
2930     if (!IS_CUSTOM_ELEMENT(i))
2931     {
2932       ei->change->target_element = EL_EMPTY_SPACE;
2933       ei->change->delay_fixed = 0;
2934       ei->change->delay_random = 0;
2935       ei->change->delay_frames = 1;
2936     }
2937
2938     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2939     {
2940       ei->has_change_event[j] = FALSE;
2941
2942       ei->event_page_nr[j] = 0;
2943       ei->event_page[j] = &ei->change_page[0];
2944     }
2945   }
2946
2947   /* add changing elements from pre-defined list */
2948   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2949   {
2950     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2951     struct ElementInfo *ei = &element_info[ch_delay->element];
2952
2953     ei->change->target_element       = ch_delay->target_element;
2954     ei->change->delay_fixed          = ch_delay->change_delay;
2955
2956     ei->change->pre_change_function  = ch_delay->pre_change_function;
2957     ei->change->change_function      = ch_delay->change_function;
2958     ei->change->post_change_function = ch_delay->post_change_function;
2959
2960     ei->change->can_change = TRUE;
2961     ei->change->can_change_or_has_action = TRUE;
2962
2963     ei->has_change_event[CE_DELAY] = TRUE;
2964
2965     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2967   }
2968
2969   /* ---------- initialize internal run-time variables --------------------- */
2970
2971   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2972   {
2973     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2974
2975     for (j = 0; j < ei->num_change_pages; j++)
2976     {
2977       ei->change_page[j].can_change_or_has_action =
2978         (ei->change_page[j].can_change |
2979          ei->change_page[j].has_action);
2980     }
2981   }
2982
2983   /* add change events from custom element configuration */
2984   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2985   {
2986     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2987
2988     for (j = 0; j < ei->num_change_pages; j++)
2989     {
2990       if (!ei->change_page[j].can_change_or_has_action)
2991         continue;
2992
2993       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2994       {
2995         /* only add event page for the first page found with this event */
2996         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2997         {
2998           ei->has_change_event[k] = TRUE;
2999
3000           ei->event_page_nr[k] = j;
3001           ei->event_page[k] = &ei->change_page[j];
3002         }
3003       }
3004     }
3005   }
3006
3007   /* ---------- initialize reference elements in change conditions --------- */
3008
3009   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3010   {
3011     int element = EL_CUSTOM_START + i;
3012     struct ElementInfo *ei = &element_info[element];
3013
3014     for (j = 0; j < ei->num_change_pages; j++)
3015     {
3016       int trigger_element = ei->change_page[j].initial_trigger_element;
3017
3018       if (trigger_element >= EL_PREV_CE_8 &&
3019           trigger_element <= EL_NEXT_CE_8)
3020         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3021
3022       ei->change_page[j].trigger_element = trigger_element;
3023     }
3024   }
3025
3026   /* ---------- initialize run-time trigger player and element ------------- */
3027
3028   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3029   {
3030     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3031
3032     for (j = 0; j < ei->num_change_pages; j++)
3033     {
3034       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3035       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3037       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3038       ei->change_page[j].actual_trigger_ce_value = 0;
3039       ei->change_page[j].actual_trigger_ce_score = 0;
3040     }
3041   }
3042
3043   /* ---------- initialize trigger events ---------------------------------- */
3044
3045   /* initialize trigger events information */
3046   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3047     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3048       trigger_events[i][j] = FALSE;
3049
3050   /* add trigger events from element change event properties */
3051   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3052   {
3053     struct ElementInfo *ei = &element_info[i];
3054
3055     for (j = 0; j < ei->num_change_pages; j++)
3056     {
3057       if (!ei->change_page[j].can_change_or_has_action)
3058         continue;
3059
3060       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3061       {
3062         int trigger_element = ei->change_page[j].trigger_element;
3063
3064         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3065         {
3066           if (ei->change_page[j].has_event[k])
3067           {
3068             if (IS_GROUP_ELEMENT(trigger_element))
3069             {
3070               struct ElementGroupInfo *group =
3071                 element_info[trigger_element].group;
3072
3073               for (l = 0; l < group->num_elements_resolved; l++)
3074                 trigger_events[group->element_resolved[l]][k] = TRUE;
3075             }
3076             else if (trigger_element == EL_ANY_ELEMENT)
3077               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3078                 trigger_events[l][k] = TRUE;
3079             else
3080               trigger_events[trigger_element][k] = TRUE;
3081           }
3082         }
3083       }
3084     }
3085   }
3086
3087   /* ---------- initialize push delay -------------------------------------- */
3088
3089   /* initialize push delay values to default */
3090   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3091   {
3092     if (!IS_CUSTOM_ELEMENT(i))
3093     {
3094       /* set default push delay values (corrected since version 3.0.7-1) */
3095       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3096       {
3097         element_info[i].push_delay_fixed = 2;
3098         element_info[i].push_delay_random = 8;
3099       }
3100       else
3101       {
3102         element_info[i].push_delay_fixed = 8;
3103         element_info[i].push_delay_random = 8;
3104       }
3105     }
3106   }
3107
3108   /* set push delay value for certain elements from pre-defined list */
3109   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3110   {
3111     int e = push_delay_list[i].element;
3112
3113     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3114     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3115   }
3116
3117   /* set push delay value for Supaplex elements for newer engine versions */
3118   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3119   {
3120     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3121     {
3122       if (IS_SP_ELEMENT(i))
3123       {
3124         /* set SP push delay to just enough to push under a falling zonk */
3125         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3126
3127         element_info[i].push_delay_fixed  = delay;
3128         element_info[i].push_delay_random = 0;
3129       }
3130     }
3131   }
3132
3133   /* ---------- initialize move stepsize ----------------------------------- */
3134
3135   /* initialize move stepsize values to default */
3136   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3137     if (!IS_CUSTOM_ELEMENT(i))
3138       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3139
3140   /* set move stepsize value for certain elements from pre-defined list */
3141   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3142   {
3143     int e = move_stepsize_list[i].element;
3144
3145     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3146   }
3147
3148   /* ---------- initialize collect score ----------------------------------- */
3149
3150   /* initialize collect score values for custom elements from initial value */
3151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3152     if (IS_CUSTOM_ELEMENT(i))
3153       element_info[i].collect_score = element_info[i].collect_score_initial;
3154
3155   /* ---------- initialize collect count ----------------------------------- */
3156
3157   /* initialize collect count values for non-custom elements */
3158   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3159     if (!IS_CUSTOM_ELEMENT(i))
3160       element_info[i].collect_count_initial = 0;
3161
3162   /* add collect count values for all elements from pre-defined list */
3163   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3164     element_info[collect_count_list[i].element].collect_count_initial =
3165       collect_count_list[i].count;
3166
3167   /* ---------- initialize access direction -------------------------------- */
3168
3169   /* initialize access direction values to default (access from every side) */
3170   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3171     if (!IS_CUSTOM_ELEMENT(i))
3172       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3173
3174   /* set access direction value for certain elements from pre-defined list */
3175   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3176     element_info[access_direction_list[i].element].access_direction =
3177       access_direction_list[i].direction;
3178
3179   /* ---------- initialize explosion content ------------------------------- */
3180   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3181   {
3182     if (IS_CUSTOM_ELEMENT(i))
3183       continue;
3184
3185     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3186     {
3187       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3188
3189       element_info[i].content.e[x][y] =
3190         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3191          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3192          i == EL_PLAYER_3 ? EL_EMERALD :
3193          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3194          i == EL_MOLE ? EL_EMERALD_RED :
3195          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3196          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3197          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3198          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3199          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3200          i == EL_WALL_EMERALD ? EL_EMERALD :
3201          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3202          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3203          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3204          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3205          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3206          i == EL_WALL_PEARL ? EL_PEARL :
3207          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3208          EL_EMPTY);
3209     }
3210   }
3211
3212   /* ---------- initialize recursion detection ------------------------------ */
3213   recursion_loop_depth = 0;
3214   recursion_loop_detected = FALSE;
3215   recursion_loop_element = EL_UNDEFINED;
3216
3217   /* ---------- initialize graphics engine ---------------------------------- */
3218   game.scroll_delay_value =
3219     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3220      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3221   game.scroll_delay_value =
3222     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3223
3224   /* ---------- initialize game engine snapshots ---------------------------- */
3225   for (i = 0; i < MAX_PLAYERS; i++)
3226     game.snapshot.last_action[i] = 0;
3227   game.snapshot.changed_action = FALSE;
3228   game.snapshot.collected_item = FALSE;
3229   game.snapshot.mode =
3230     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3231      SNAPSHOT_MODE_EVERY_STEP :
3232      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3233      SNAPSHOT_MODE_EVERY_MOVE :
3234      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3235      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3236   game.snapshot.save_snapshot = FALSE;
3237
3238   /* ---------- initialize level time for Supaplex engine ------------------- */
3239   /* Supaplex levels with time limit currently unsupported -- should be added */
3240   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3241     level.time = 0;
3242 }
3243
3244 int get_num_special_action(int element, int action_first, int action_last)
3245 {
3246   int num_special_action = 0;
3247   int i, j;
3248
3249   for (i = action_first; i <= action_last; i++)
3250   {
3251     boolean found = FALSE;
3252
3253     for (j = 0; j < NUM_DIRECTIONS; j++)
3254       if (el_act_dir2img(element, i, j) !=
3255           el_act_dir2img(element, ACTION_DEFAULT, j))
3256         found = TRUE;
3257
3258     if (found)
3259       num_special_action++;
3260     else
3261       break;
3262   }
3263
3264   return num_special_action;
3265 }
3266
3267
3268 /*
3269   =============================================================================
3270   InitGame()
3271   -----------------------------------------------------------------------------
3272   initialize and start new game
3273   =============================================================================
3274 */
3275
3276 #if DEBUG_INIT_PLAYER
3277 static void DebugPrintPlayerStatus(char *message)
3278 {
3279   int i;
3280
3281   if (!options.debug)
3282     return;
3283
3284   printf("%s:\n", message);
3285
3286   for (i = 0; i < MAX_PLAYERS; i++)
3287   {
3288     struct PlayerInfo *player = &stored_player[i];
3289
3290     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3291            i + 1,
3292            player->present,
3293            player->connected,
3294            player->connected_locally,
3295            player->connected_network,
3296            player->active);
3297
3298     if (local_player == player)
3299       printf(" (local player)");
3300
3301     printf("\n");
3302   }
3303 }
3304 #endif
3305
3306 void InitGame()
3307 {
3308   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3309   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3310   int fade_mask = REDRAW_FIELD;
3311
3312   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3313   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3314   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3315   int initial_move_dir = MV_DOWN;
3316   int i, j, x, y;
3317
3318   // required here to update video display before fading (FIX THIS)
3319   DrawMaskedBorder(REDRAW_DOOR_2);
3320
3321   if (!game.restart_level)
3322     CloseDoor(DOOR_CLOSE_1);
3323
3324   SetGameStatus(GAME_MODE_PLAYING);
3325
3326   if (level_editor_test_game)
3327     FadeSkipNextFadeIn();
3328   else
3329     FadeSetEnterScreen();
3330
3331   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3332     fade_mask = REDRAW_ALL;
3333
3334   FadeLevelSoundsAndMusic();
3335
3336   ExpireSoundLoops(TRUE);
3337
3338   if (!level_editor_test_game)
3339     FadeOut(fade_mask);
3340
3341   /* needed if different viewport properties defined for playing */
3342   ChangeViewportPropertiesIfNeeded();
3343
3344   ClearField();
3345
3346   DrawCompleteVideoDisplay();
3347
3348   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3349
3350   InitGameEngine();
3351   InitGameControlValues();
3352
3353   /* don't play tapes over network */
3354   network_playing = (network.enabled && !tape.playing);
3355
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357   {
3358     struct PlayerInfo *player = &stored_player[i];
3359
3360     player->index_nr = i;
3361     player->index_bit = (1 << i);
3362     player->element_nr = EL_PLAYER_1 + i;
3363
3364     player->present = FALSE;
3365     player->active = FALSE;
3366     player->mapped = FALSE;
3367
3368     player->killed = FALSE;
3369     player->reanimated = FALSE;
3370
3371     player->action = 0;
3372     player->effective_action = 0;
3373     player->programmed_action = 0;
3374
3375     player->mouse_action.lx = 0;
3376     player->mouse_action.ly = 0;
3377     player->mouse_action.button = 0;
3378     player->mouse_action.button_hint = 0;
3379
3380     player->effective_mouse_action.lx = 0;
3381     player->effective_mouse_action.ly = 0;
3382     player->effective_mouse_action.button = 0;
3383     player->effective_mouse_action.button_hint = 0;
3384
3385     player->score = 0;
3386     player->score_final = 0;
3387
3388     player->health = MAX_HEALTH;
3389     player->health_final = MAX_HEALTH;
3390
3391     player->gems_still_needed = level.gems_needed;
3392     player->sokobanfields_still_needed = 0;
3393     player->lights_still_needed = 0;
3394     player->friends_still_needed = 0;
3395
3396     for (j = 0; j < MAX_NUM_KEYS; j++)
3397       player->key[j] = FALSE;
3398
3399     player->num_white_keys = 0;
3400
3401     player->dynabomb_count = 0;
3402     player->dynabomb_size = 1;
3403     player->dynabombs_left = 0;
3404     player->dynabomb_xl = FALSE;
3405
3406     player->MovDir = initial_move_dir;
3407     player->MovPos = 0;
3408     player->GfxPos = 0;
3409     player->GfxDir = initial_move_dir;
3410     player->GfxAction = ACTION_DEFAULT;
3411     player->Frame = 0;
3412     player->StepFrame = 0;
3413
3414     player->initial_element = player->element_nr;
3415     player->artwork_element =
3416       (level.use_artwork_element[i] ? level.artwork_element[i] :
3417        player->element_nr);
3418     player->use_murphy = FALSE;
3419
3420     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3421     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3422
3423     player->gravity = level.initial_player_gravity[i];
3424
3425     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3426
3427     player->actual_frame_counter = 0;
3428
3429     player->step_counter = 0;
3430
3431     player->last_move_dir = initial_move_dir;
3432
3433     player->is_active = FALSE;
3434
3435     player->is_waiting = FALSE;
3436     player->is_moving = FALSE;
3437     player->is_auto_moving = FALSE;
3438     player->is_digging = FALSE;
3439     player->is_snapping = FALSE;
3440     player->is_collecting = FALSE;
3441     player->is_pushing = FALSE;
3442     player->is_switching = FALSE;
3443     player->is_dropping = FALSE;
3444     player->is_dropping_pressed = FALSE;
3445
3446     player->is_bored = FALSE;
3447     player->is_sleeping = FALSE;
3448
3449     player->was_waiting = TRUE;
3450     player->was_moving = FALSE;
3451     player->was_snapping = FALSE;
3452     player->was_dropping = FALSE;
3453
3454     player->force_dropping = FALSE;
3455
3456     player->frame_counter_bored = -1;
3457     player->frame_counter_sleeping = -1;
3458
3459     player->anim_delay_counter = 0;
3460     player->post_delay_counter = 0;
3461
3462     player->dir_waiting = initial_move_dir;
3463     player->action_waiting = ACTION_DEFAULT;
3464     player->last_action_waiting = ACTION_DEFAULT;
3465     player->special_action_bored = ACTION_DEFAULT;
3466     player->special_action_sleeping = ACTION_DEFAULT;
3467
3468     player->switch_x = -1;
3469     player->switch_y = -1;
3470
3471     player->drop_x = -1;
3472     player->drop_y = -1;
3473
3474     player->show_envelope = 0;
3475
3476     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3477
3478     player->push_delay       = -1;      /* initialized when pushing starts */
3479     player->push_delay_value = game.initial_push_delay_value;
3480
3481     player->drop_delay = 0;
3482     player->drop_pressed_delay = 0;
3483
3484     player->last_jx = -1;
3485     player->last_jy = -1;
3486     player->jx = -1;
3487     player->jy = -1;
3488
3489     player->shield_normal_time_left = 0;
3490     player->shield_deadly_time_left = 0;
3491
3492     player->inventory_infinite_element = EL_UNDEFINED;
3493     player->inventory_size = 0;
3494
3495     if (level.use_initial_inventory[i])
3496     {
3497       for (j = 0; j < level.initial_inventory_size[i]; j++)
3498       {
3499         int element = level.initial_inventory_content[i][j];
3500         int collect_count = element_info[element].collect_count_initial;
3501         int k;
3502
3503         if (!IS_CUSTOM_ELEMENT(element))
3504           collect_count = 1;
3505
3506         if (collect_count == 0)
3507           player->inventory_infinite_element = element;
3508         else
3509           for (k = 0; k < collect_count; k++)
3510             if (player->inventory_size < MAX_INVENTORY_SIZE)
3511               player->inventory_element[player->inventory_size++] = element;
3512       }
3513     }
3514
3515     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3516     SnapField(player, 0, 0);
3517
3518     player->LevelSolved = FALSE;
3519     player->GameOver = FALSE;
3520
3521     player->LevelSolved_GameWon = FALSE;
3522     player->LevelSolved_GameEnd = FALSE;
3523     player->LevelSolved_PanelOff = FALSE;
3524     player->LevelSolved_SaveTape = FALSE;
3525     player->LevelSolved_SaveScore = FALSE;
3526
3527     player->LevelSolved_CountingTime = 0;
3528     player->LevelSolved_CountingScore = 0;
3529     player->LevelSolved_CountingHealth = 0;
3530
3531     map_player_action[i] = i;
3532   }
3533
3534   network_player_action_received = FALSE;
3535
3536   /* initial null action */
3537   if (network_playing)
3538     SendToServer_MovePlayer(MV_NONE);
3539
3540   ZX = ZY = -1;
3541   ExitX = ExitY = -1;
3542
3543   FrameCounter = 0;
3544   TimeFrames = 0;
3545   TimePlayed = 0;
3546   TimeLeft = level.time;
3547   TapeTime = 0;
3548
3549   ScreenMovDir = MV_NONE;
3550   ScreenMovPos = 0;
3551   ScreenGfxPos = 0;
3552
3553   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3554
3555   AllPlayersGone = FALSE;
3556
3557   game.no_time_limit = (level.time == 0);
3558
3559   game.yamyam_content_nr = 0;
3560   game.robot_wheel_active = FALSE;
3561   game.magic_wall_active = FALSE;
3562   game.magic_wall_time_left = 0;
3563   game.light_time_left = 0;
3564   game.timegate_time_left = 0;
3565   game.switchgate_pos = 0;
3566   game.wind_direction = level.wind_direction_initial;
3567
3568   game.lenses_time_left = 0;
3569   game.magnify_time_left = 0;
3570
3571   game.ball_state = level.ball_state_initial;
3572   game.ball_content_nr = 0;
3573
3574   game.envelope_active = FALSE;
3575
3576   for (i = 0; i < NUM_BELTS; i++)
3577   {
3578     game.belt_dir[i] = MV_NONE;
3579     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3580   }
3581
3582   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3583     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3584
3585 #if DEBUG_INIT_PLAYER
3586   DebugPrintPlayerStatus("Player status at level initialization");
3587 #endif
3588
3589   SCAN_PLAYFIELD(x, y)
3590   {
3591     Feld[x][y] = level.field[x][y];
3592     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3593     ChangeDelay[x][y] = 0;
3594     ChangePage[x][y] = -1;
3595     CustomValue[x][y] = 0;              /* initialized in InitField() */
3596     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3597     AmoebaNr[x][y] = 0;
3598     WasJustMoving[x][y] = 0;
3599     WasJustFalling[x][y] = 0;
3600     CheckCollision[x][y] = 0;
3601     CheckImpact[x][y] = 0;
3602     Stop[x][y] = FALSE;
3603     Pushed[x][y] = FALSE;
3604
3605     ChangeCount[x][y] = 0;
3606     ChangeEvent[x][y] = -1;
3607
3608     ExplodePhase[x][y] = 0;
3609     ExplodeDelay[x][y] = 0;
3610     ExplodeField[x][y] = EX_TYPE_NONE;
3611
3612     RunnerVisit[x][y] = 0;
3613     PlayerVisit[x][y] = 0;
3614
3615     GfxFrame[x][y] = 0;
3616     GfxRandom[x][y] = INIT_GFX_RANDOM();
3617     GfxElement[x][y] = EL_UNDEFINED;
3618     GfxAction[x][y] = ACTION_DEFAULT;
3619     GfxDir[x][y] = MV_NONE;
3620     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3621   }
3622
3623   SCAN_PLAYFIELD(x, y)
3624   {
3625     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3626       emulate_bd = FALSE;
3627     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3628       emulate_sb = FALSE;
3629     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3630       emulate_sp = FALSE;
3631
3632     InitField(x, y, TRUE);
3633
3634     ResetGfxAnimation(x, y);
3635   }
3636
3637   InitBeltMovement();
3638
3639   for (i = 0; i < MAX_PLAYERS; i++)
3640   {
3641     struct PlayerInfo *player = &stored_player[i];
3642
3643     /* set number of special actions for bored and sleeping animation */
3644     player->num_special_action_bored =
3645       get_num_special_action(player->artwork_element,
3646                              ACTION_BORING_1, ACTION_BORING_LAST);
3647     player->num_special_action_sleeping =
3648       get_num_special_action(player->artwork_element,
3649                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3650   }
3651
3652   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3653                     emulate_sb ? EMU_SOKOBAN :
3654                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3655
3656   /* initialize type of slippery elements */
3657   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3658   {
3659     if (!IS_CUSTOM_ELEMENT(i))
3660     {
3661       /* default: elements slip down either to the left or right randomly */
3662       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3663
3664       /* SP style elements prefer to slip down on the left side */
3665       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3666         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3667
3668       /* BD style elements prefer to slip down on the left side */
3669       if (game.emulation == EMU_BOULDERDASH)
3670         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3671     }
3672   }
3673
3674   /* initialize explosion and ignition delay */
3675   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3676   {
3677     if (!IS_CUSTOM_ELEMENT(i))
3678     {
3679       int num_phase = 8;
3680       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3681                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3682                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3683       int last_phase = (num_phase + 1) * delay;
3684       int half_phase = (num_phase / 2) * delay;
3685
3686       element_info[i].explosion_delay = last_phase - 1;
3687       element_info[i].ignition_delay = half_phase;
3688
3689       if (i == EL_BLACK_ORB)
3690         element_info[i].ignition_delay = 1;
3691     }
3692   }
3693
3694   /* correct non-moving belts to start moving left */
3695   for (i = 0; i < NUM_BELTS; i++)
3696     if (game.belt_dir[i] == MV_NONE)
3697       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3698
3699 #if USE_NEW_PLAYER_ASSIGNMENTS
3700   for (i = 0; i < MAX_PLAYERS; i++)
3701   {
3702     stored_player[i].connected = FALSE;
3703
3704     /* in network game mode, the local player might not be the first player */
3705     if (stored_player[i].connected_locally)
3706       local_player = &stored_player[i];
3707   }
3708
3709   if (!network.enabled)
3710     local_player->connected = TRUE;
3711
3712   if (tape.playing)
3713   {
3714     for (i = 0; i < MAX_PLAYERS; i++)
3715       stored_player[i].connected = tape.player_participates[i];
3716   }
3717   else if (network.enabled)
3718   {
3719     /* add team mode players connected over the network (needed for correct
3720        assignment of player figures from level to locally playing players) */
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723       if (stored_player[i].connected_network)
3724         stored_player[i].connected = TRUE;
3725   }
3726   else if (game.team_mode)
3727   {
3728     /* try to guess locally connected team mode players (needed for correct
3729        assignment of player figures from level to locally playing players) */
3730
3731     for (i = 0; i < MAX_PLAYERS; i++)
3732       if (setup.input[i].use_joystick ||
3733           setup.input[i].key.left != KSYM_UNDEFINED)
3734         stored_player[i].connected = TRUE;
3735   }
3736
3737 #if DEBUG_INIT_PLAYER
3738   DebugPrintPlayerStatus("Player status after level initialization");
3739 #endif
3740
3741 #if DEBUG_INIT_PLAYER
3742   if (options.debug)
3743     printf("Reassigning players ...\n");
3744 #endif
3745
3746   /* check if any connected player was not found in playfield */
3747   for (i = 0; i < MAX_PLAYERS; i++)
3748   {
3749     struct PlayerInfo *player = &stored_player[i];
3750
3751     if (player->connected && !player->present)
3752     {
3753       struct PlayerInfo *field_player = NULL;
3754
3755 #if DEBUG_INIT_PLAYER
3756       if (options.debug)
3757         printf("- looking for field player for player %d ...\n", i + 1);
3758 #endif
3759
3760       /* assign first free player found that is present in the playfield */
3761
3762       /* first try: look for unmapped playfield player that is not connected */
3763       for (j = 0; j < MAX_PLAYERS; j++)
3764         if (field_player == NULL &&
3765             stored_player[j].present &&
3766             !stored_player[j].mapped &&
3767             !stored_player[j].connected)
3768           field_player = &stored_player[j];
3769
3770       /* second try: look for *any* unmapped playfield player */
3771       for (j = 0; j < MAX_PLAYERS; j++)
3772         if (field_player == NULL &&
3773             stored_player[j].present &&
3774             !stored_player[j].mapped)
3775           field_player = &stored_player[j];
3776
3777       if (field_player != NULL)
3778       {
3779         int jx = field_player->jx, jy = field_player->jy;
3780
3781 #if DEBUG_INIT_PLAYER
3782         if (options.debug)
3783           printf("- found player %d\n", field_player->index_nr + 1);
3784 #endif
3785
3786         player->present = FALSE;
3787         player->active = FALSE;
3788
3789         field_player->present = TRUE;
3790         field_player->active = TRUE;
3791
3792         /*
3793         player->initial_element = field_player->initial_element;
3794         player->artwork_element = field_player->artwork_element;
3795
3796         player->block_last_field       = field_player->block_last_field;
3797         player->block_delay_adjustment = field_player->block_delay_adjustment;
3798         */
3799
3800         StorePlayer[jx][jy] = field_player->element_nr;
3801
3802         field_player->jx = field_player->last_jx = jx;
3803         field_player->jy = field_player->last_jy = jy;
3804
3805         if (local_player == player)
3806           local_player = field_player;
3807
3808         map_player_action[field_player->index_nr] = i;
3809
3810         field_player->mapped = TRUE;
3811
3812 #if DEBUG_INIT_PLAYER
3813         if (options.debug)
3814           printf("- map_player_action[%d] == %d\n",
3815                  field_player->index_nr + 1, i + 1);
3816 #endif
3817       }
3818     }
3819
3820     if (player->connected && player->present)
3821       player->mapped = TRUE;
3822   }
3823
3824 #if DEBUG_INIT_PLAYER
3825   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3826 #endif
3827
3828 #else
3829
3830   /* check if any connected player was not found in playfield */
3831   for (i = 0; i < MAX_PLAYERS; i++)
3832   {
3833     struct PlayerInfo *player = &stored_player[i];
3834
3835     if (player->connected && !player->present)
3836     {
3837       for (j = 0; j < MAX_PLAYERS; j++)
3838       {
3839         struct PlayerInfo *field_player = &stored_player[j];
3840         int jx = field_player->jx, jy = field_player->jy;
3841
3842         /* assign first free player found that is present in the playfield */
3843         if (field_player->present && !field_player->connected)
3844         {
3845           player->present = TRUE;
3846           player->active = TRUE;
3847
3848           field_player->present = FALSE;
3849           field_player->active = FALSE;
3850
3851           player->initial_element = field_player->initial_element;
3852           player->artwork_element = field_player->artwork_element;
3853
3854           player->block_last_field       = field_player->block_last_field;
3855           player->block_delay_adjustment = field_player->block_delay_adjustment;
3856
3857           StorePlayer[jx][jy] = player->element_nr;
3858
3859           player->jx = player->last_jx = jx;
3860           player->jy = player->last_jy = jy;
3861
3862           break;
3863         }
3864       }
3865     }
3866   }
3867 #endif
3868
3869 #if 0
3870   printf("::: local_player->present == %d\n", local_player->present);
3871 #endif
3872
3873   /* set focus to local player for network games, else to all players */
3874   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3875   game.centered_player_nr_next = game.centered_player_nr;
3876   game.set_centered_player = FALSE;
3877
3878   if (network_playing && tape.recording)
3879   {
3880     /* store client dependent player focus when recording network games */
3881     tape.centered_player_nr_next = game.centered_player_nr_next;
3882     tape.set_centered_player = TRUE;
3883   }
3884
3885   if (tape.playing)
3886   {
3887     /* when playing a tape, eliminate all players who do not participate */
3888
3889 #if USE_NEW_PLAYER_ASSIGNMENTS
3890
3891     if (!game.team_mode)
3892     {
3893       for (i = 0; i < MAX_PLAYERS; i++)
3894       {
3895         if (stored_player[i].active &&
3896             !tape.player_participates[map_player_action[i]])
3897         {
3898           struct PlayerInfo *player = &stored_player[i];
3899           int jx = player->jx, jy = player->jy;
3900
3901 #if DEBUG_INIT_PLAYER
3902           if (options.debug)
3903             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3904 #endif
3905
3906           player->active = FALSE;
3907           StorePlayer[jx][jy] = 0;
3908           Feld[jx][jy] = EL_EMPTY;
3909         }
3910       }
3911     }
3912
3913 #else
3914
3915     for (i = 0; i < MAX_PLAYERS; i++)
3916     {
3917       if (stored_player[i].active &&
3918           !tape.player_participates[i])
3919       {
3920         struct PlayerInfo *player = &stored_player[i];
3921         int jx = player->jx, jy = player->jy;
3922
3923         player->active = FALSE;
3924         StorePlayer[jx][jy] = 0;
3925         Feld[jx][jy] = EL_EMPTY;
3926       }
3927     }
3928 #endif
3929   }
3930   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3931   {
3932     /* when in single player mode, eliminate all but the local player */
3933
3934     for (i = 0; i < MAX_PLAYERS; i++)
3935     {
3936       struct PlayerInfo *player = &stored_player[i];
3937
3938       if (player->active && player != local_player)
3939       {
3940         int jx = player->jx, jy = player->jy;
3941
3942         player->active = FALSE;
3943         player->present = FALSE;
3944
3945         StorePlayer[jx][jy] = 0;
3946         Feld[jx][jy] = EL_EMPTY;
3947       }
3948     }
3949   }
3950
3951   /* when recording the game, store which players take part in the game */
3952   if (tape.recording)
3953   {
3954 #if USE_NEW_PLAYER_ASSIGNMENTS
3955     for (i = 0; i < MAX_PLAYERS; i++)
3956       if (stored_player[i].connected)
3957         tape.player_participates[i] = TRUE;
3958 #else
3959     for (i = 0; i < MAX_PLAYERS; i++)
3960       if (stored_player[i].active)
3961         tape.player_participates[i] = TRUE;
3962 #endif
3963   }
3964
3965 #if DEBUG_INIT_PLAYER
3966   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3967 #endif
3968
3969   if (BorderElement == EL_EMPTY)
3970   {
3971     SBX_Left = 0;
3972     SBX_Right = lev_fieldx - SCR_FIELDX;
3973     SBY_Upper = 0;
3974     SBY_Lower = lev_fieldy - SCR_FIELDY;
3975   }
3976   else
3977   {
3978     SBX_Left = -1;
3979     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3980     SBY_Upper = -1;
3981     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3982   }
3983
3984   if (full_lev_fieldx <= SCR_FIELDX)
3985     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3986   if (full_lev_fieldy <= SCR_FIELDY)
3987     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3988
3989   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3990     SBX_Left--;
3991   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3992     SBY_Upper--;
3993
3994   /* if local player not found, look for custom element that might create
3995      the player (make some assumptions about the right custom element) */
3996   if (!local_player->present)
3997   {
3998     int start_x = 0, start_y = 0;
3999     int found_rating = 0;
4000     int found_element = EL_UNDEFINED;
4001     int player_nr = local_player->index_nr;
4002
4003     SCAN_PLAYFIELD(x, y)
4004     {
4005       int element = Feld[x][y];
4006       int content;
4007       int xx, yy;
4008       boolean is_player;
4009
4010       if (level.use_start_element[player_nr] &&
4011           level.start_element[player_nr] == element &&
4012           found_rating < 4)
4013       {
4014         start_x = x;
4015         start_y = y;
4016
4017         found_rating = 4;
4018         found_element = element;
4019       }
4020
4021       if (!IS_CUSTOM_ELEMENT(element))
4022         continue;
4023
4024       if (CAN_CHANGE(element))
4025       {
4026         for (i = 0; i < element_info[element].num_change_pages; i++)
4027         {
4028           /* check for player created from custom element as single target */
4029           content = element_info[element].change_page[i].target_element;
4030           is_player = ELEM_IS_PLAYER(content);
4031
4032           if (is_player && (found_rating < 3 ||
4033                             (found_rating == 3 && element < found_element)))
4034           {
4035             start_x = x;
4036             start_y = y;
4037
4038             found_rating = 3;
4039             found_element = element;
4040           }
4041         }
4042       }
4043
4044       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4045       {
4046         /* check for player created from custom element as explosion content */
4047         content = element_info[element].content.e[xx][yy];
4048         is_player = ELEM_IS_PLAYER(content);
4049
4050         if (is_player && (found_rating < 2 ||
4051                           (found_rating == 2 && element < found_element)))
4052         {
4053           start_x = x + xx - 1;
4054           start_y = y + yy - 1;
4055
4056           found_rating = 2;
4057           found_element = element;
4058         }
4059
4060         if (!CAN_CHANGE(element))
4061           continue;
4062
4063         for (i = 0; i < element_info[element].num_change_pages; i++)
4064         {
4065           /* check for player created from custom element as extended target */
4066           content =
4067             element_info[element].change_page[i].target_content.e[xx][yy];
4068
4069           is_player = ELEM_IS_PLAYER(content);
4070
4071           if (is_player && (found_rating < 1 ||
4072                             (found_rating == 1 && element < found_element)))
4073           {
4074             start_x = x + xx - 1;
4075             start_y = y + yy - 1;
4076
4077             found_rating = 1;
4078             found_element = element;
4079           }
4080         }
4081       }
4082     }
4083
4084     scroll_x = SCROLL_POSITION_X(start_x);
4085     scroll_y = SCROLL_POSITION_Y(start_y);
4086   }
4087   else
4088   {
4089     scroll_x = SCROLL_POSITION_X(local_player->jx);
4090     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4091   }
4092
4093   /* !!! FIX THIS (START) !!! */
4094   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4095   {
4096     InitGameEngine_EM();
4097   }
4098   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4099   {
4100     InitGameEngine_SP();
4101   }
4102   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4103   {
4104     InitGameEngine_MM();
4105   }
4106   else
4107   {
4108     DrawLevel(REDRAW_FIELD);
4109     DrawAllPlayers();
4110
4111     /* after drawing the level, correct some elements */
4112     if (game.timegate_time_left == 0)
4113       CloseAllOpenTimegates();
4114   }
4115
4116   /* blit playfield from scroll buffer to normal back buffer for fading in */
4117   BlitScreenToBitmap(backbuffer);
4118   /* !!! FIX THIS (END) !!! */
4119
4120   DrawMaskedBorder(fade_mask);
4121
4122   FadeIn(fade_mask);
4123
4124 #if 1
4125   // full screen redraw is required at this point in the following cases:
4126   // - special editor door undrawn when game was started from level editor
4127   // - drawing area (playfield) was changed and has to be removed completely
4128   redraw_mask = REDRAW_ALL;
4129   BackToFront();
4130 #endif
4131
4132   if (!game.restart_level)
4133   {
4134     /* copy default game door content to main double buffer */
4135
4136     /* !!! CHECK AGAIN !!! */
4137     SetPanelBackground();
4138     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4139     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4140   }
4141
4142   SetPanelBackground();
4143   SetDrawBackgroundMask(REDRAW_DOOR_1);
4144
4145   UpdateAndDisplayGameControlValues();
4146
4147   if (!game.restart_level)
4148   {
4149     UnmapGameButtons();
4150     UnmapTapeButtons();
4151
4152     FreeGameButtons();
4153     CreateGameButtons();
4154
4155     MapGameButtons();
4156     MapTapeButtons();
4157
4158     /* copy actual game door content to door double buffer for OpenDoor() */
4159     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4160
4161     OpenDoor(DOOR_OPEN_ALL);
4162
4163     KeyboardAutoRepeatOffUnlessAutoplay();
4164
4165 #if DEBUG_INIT_PLAYER
4166     DebugPrintPlayerStatus("Player status (final)");
4167 #endif
4168   }
4169
4170   UnmapAllGadgets();
4171
4172   MapGameButtons();
4173   MapTapeButtons();
4174
4175   if (!game.restart_level && !tape.playing)
4176   {
4177     LevelStats_incPlayed(level_nr);
4178
4179     SaveLevelSetup_SeriesInfo();
4180   }
4181
4182   game.restart_level = FALSE;
4183   game.restart_game_message = NULL;
4184
4185   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4186     InitGameActions_MM();
4187
4188   SaveEngineSnapshotToListInitial();
4189
4190   if (!game.restart_level)
4191   {
4192     PlaySound(SND_GAME_STARTING);
4193
4194     if (setup.sound_music)
4195       PlayLevelMusic();
4196   }
4197 }
4198
4199 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4200                         int actual_player_x, int actual_player_y)
4201 {
4202   /* this is used for non-R'n'D game engines to update certain engine values */
4203
4204   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4205   {
4206     actual_player_x = correctLevelPosX_EM(actual_player_x);
4207     actual_player_y = correctLevelPosY_EM(actual_player_y);
4208   }
4209
4210   /* needed to determine if sounds are played within the visible screen area */
4211   scroll_x = actual_scroll_x;
4212   scroll_y = actual_scroll_y;
4213
4214   /* needed to get player position for "follow finger" playing input method */
4215   local_player->jx = actual_player_x;
4216   local_player->jy = actual_player_y;
4217 }
4218
4219 void InitMovDir(int x, int y)
4220 {
4221   int i, element = Feld[x][y];
4222   static int xy[4][2] =
4223   {
4224     {  0, +1 },
4225     { +1,  0 },
4226     {  0, -1 },
4227     { -1,  0 }
4228   };
4229   static int direction[3][4] =
4230   {
4231     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4232     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4233     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4234   };
4235
4236   switch (element)
4237   {
4238     case EL_BUG_RIGHT:
4239     case EL_BUG_UP:
4240     case EL_BUG_LEFT:
4241     case EL_BUG_DOWN:
4242       Feld[x][y] = EL_BUG;
4243       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4244       break;
4245
4246     case EL_SPACESHIP_RIGHT:
4247     case EL_SPACESHIP_UP:
4248     case EL_SPACESHIP_LEFT:
4249     case EL_SPACESHIP_DOWN:
4250       Feld[x][y] = EL_SPACESHIP;
4251       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4252       break;
4253
4254     case EL_BD_BUTTERFLY_RIGHT:
4255     case EL_BD_BUTTERFLY_UP:
4256     case EL_BD_BUTTERFLY_LEFT:
4257     case EL_BD_BUTTERFLY_DOWN:
4258       Feld[x][y] = EL_BD_BUTTERFLY;
4259       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4260       break;
4261
4262     case EL_BD_FIREFLY_RIGHT:
4263     case EL_BD_FIREFLY_UP:
4264     case EL_BD_FIREFLY_LEFT:
4265     case EL_BD_FIREFLY_DOWN:
4266       Feld[x][y] = EL_BD_FIREFLY;
4267       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4268       break;
4269
4270     case EL_PACMAN_RIGHT:
4271     case EL_PACMAN_UP:
4272     case EL_PACMAN_LEFT:
4273     case EL_PACMAN_DOWN:
4274       Feld[x][y] = EL_PACMAN;
4275       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4276       break;
4277
4278     case EL_YAMYAM_LEFT:
4279     case EL_YAMYAM_RIGHT:
4280     case EL_YAMYAM_UP:
4281     case EL_YAMYAM_DOWN:
4282       Feld[x][y] = EL_YAMYAM;
4283       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4284       break;
4285
4286     case EL_SP_SNIKSNAK:
4287       MovDir[x][y] = MV_UP;
4288       break;
4289
4290     case EL_SP_ELECTRON:
4291       MovDir[x][y] = MV_LEFT;
4292       break;
4293
4294     case EL_MOLE_LEFT:
4295     case EL_MOLE_RIGHT:
4296     case EL_MOLE_UP:
4297     case EL_MOLE_DOWN:
4298       Feld[x][y] = EL_MOLE;
4299       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4300       break;
4301
4302     default:
4303       if (IS_CUSTOM_ELEMENT(element))
4304       {
4305         struct ElementInfo *ei = &element_info[element];
4306         int move_direction_initial = ei->move_direction_initial;
4307         int move_pattern = ei->move_pattern;
4308
4309         if (move_direction_initial == MV_START_PREVIOUS)
4310         {
4311           if (MovDir[x][y] != MV_NONE)
4312             return;
4313
4314           move_direction_initial = MV_START_AUTOMATIC;
4315         }
4316
4317         if (move_direction_initial == MV_START_RANDOM)
4318           MovDir[x][y] = 1 << RND(4);
4319         else if (move_direction_initial & MV_ANY_DIRECTION)
4320           MovDir[x][y] = move_direction_initial;
4321         else if (move_pattern == MV_ALL_DIRECTIONS ||
4322                  move_pattern == MV_TURNING_LEFT ||
4323                  move_pattern == MV_TURNING_RIGHT ||
4324                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4325                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4326                  move_pattern == MV_TURNING_RANDOM)
4327           MovDir[x][y] = 1 << RND(4);
4328         else if (move_pattern == MV_HORIZONTAL)
4329           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4330         else if (move_pattern == MV_VERTICAL)
4331           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4332         else if (move_pattern & MV_ANY_DIRECTION)
4333           MovDir[x][y] = element_info[element].move_pattern;
4334         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4335                  move_pattern == MV_ALONG_RIGHT_SIDE)
4336         {
4337           /* use random direction as default start direction */
4338           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4339             MovDir[x][y] = 1 << RND(4);
4340
4341           for (i = 0; i < NUM_DIRECTIONS; i++)
4342           {
4343             int x1 = x + xy[i][0];
4344             int y1 = y + xy[i][1];
4345
4346             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4347             {
4348               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4349                 MovDir[x][y] = direction[0][i];
4350               else
4351                 MovDir[x][y] = direction[1][i];
4352
4353               break;
4354             }
4355           }
4356         }                
4357       }
4358       else
4359       {
4360         MovDir[x][y] = 1 << RND(4);
4361
4362         if (element != EL_BUG &&
4363             element != EL_SPACESHIP &&
4364             element != EL_BD_BUTTERFLY &&
4365             element != EL_BD_FIREFLY)
4366           break;
4367
4368         for (i = 0; i < NUM_DIRECTIONS; i++)
4369         {
4370           int x1 = x + xy[i][0];
4371           int y1 = y + xy[i][1];
4372
4373           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4374           {
4375             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4376             {
4377               MovDir[x][y] = direction[0][i];
4378               break;
4379             }
4380             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4381                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4382             {
4383               MovDir[x][y] = direction[1][i];
4384               break;
4385             }
4386           }
4387         }
4388       }
4389       break;
4390   }
4391
4392   GfxDir[x][y] = MovDir[x][y];
4393 }
4394
4395 void InitAmoebaNr(int x, int y)
4396 {
4397   int i;
4398   int group_nr = AmoebeNachbarNr(x, y);
4399
4400   if (group_nr == 0)
4401   {
4402     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4403     {
4404       if (AmoebaCnt[i] == 0)
4405       {
4406         group_nr = i;
4407         break;
4408       }
4409     }
4410   }
4411
4412   AmoebaNr[x][y] = group_nr;
4413   AmoebaCnt[group_nr]++;
4414   AmoebaCnt2[group_nr]++;
4415 }
4416
4417 static void PlayerWins(struct PlayerInfo *player)
4418 {
4419   player->LevelSolved = TRUE;
4420   player->GameOver = TRUE;
4421
4422   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4423                          level.native_em_level->lev->score :
4424                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4425                          game_mm.score :
4426                          player->score);
4427   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4428                           MM_HEALTH(game_mm.laser_overload_value) :
4429                           player->health);
4430
4431   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4432                                       TimeLeft);
4433   player->LevelSolved_CountingScore = player->score_final;
4434   player->LevelSolved_CountingHealth = player->health_final;
4435 }
4436
4437 void GameWon()
4438 {
4439   static int time_count_steps;
4440   static int time, time_final;
4441   static int score, score_final;
4442   static int health, health_final;
4443   static int game_over_delay_1 = 0;
4444   static int game_over_delay_2 = 0;
4445   static int game_over_delay_3 = 0;
4446   int game_over_delay_value_1 = 50;
4447   int game_over_delay_value_2 = 25;
4448   int game_over_delay_value_3 = 50;
4449
4450   if (!local_player->LevelSolved_GameWon)
4451   {
4452     int i;
4453
4454     /* do not start end game actions before the player stops moving (to exit) */
4455     if (local_player->MovPos)
4456       return;
4457
4458     local_player->LevelSolved_GameWon = TRUE;
4459     local_player->LevelSolved_SaveTape = tape.recording;
4460     local_player->LevelSolved_SaveScore = !tape.playing;
4461
4462     if (!tape.playing)
4463     {
4464       LevelStats_incSolved(level_nr);
4465
4466       SaveLevelSetup_SeriesInfo();
4467     }
4468
4469     if (tape.auto_play)         /* tape might already be stopped here */
4470       tape.auto_play_level_solved = TRUE;
4471
4472     TapeStop();
4473
4474     game_over_delay_1 = 0;
4475     game_over_delay_2 = 0;
4476     game_over_delay_3 = game_over_delay_value_3;
4477
4478     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4479     score = score_final = local_player->score_final;
4480     health = health_final = local_player->health_final;
4481
4482     if (level.score[SC_TIME_BONUS] > 0)
4483     {
4484       if (TimeLeft > 0)
4485       {
4486         time_final = 0;
4487         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4488       }
4489       else if (game.no_time_limit && TimePlayed < 999)
4490       {
4491         time_final = 999;
4492         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4493       }
4494
4495       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4496
4497       game_over_delay_1 = game_over_delay_value_1;
4498
4499       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4500       {
4501         health_final = 0;
4502         score_final += health * level.score[SC_TIME_BONUS];
4503
4504         game_over_delay_2 = game_over_delay_value_2;
4505       }
4506
4507       local_player->score_final = score_final;
4508       local_player->health_final = health_final;
4509     }
4510
4511     if (level_editor_test_game)
4512     {
4513       time = time_final;
4514       score = score_final;
4515
4516       local_player->LevelSolved_CountingTime = time;
4517       local_player->LevelSolved_CountingScore = score;
4518
4519       game_panel_controls[GAME_PANEL_TIME].value = time;
4520       game_panel_controls[GAME_PANEL_SCORE].value = score;
4521
4522       DisplayGameControlValues();
4523     }
4524
4525     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4526     {
4527       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4528       {
4529         /* close exit door after last player */
4530         if ((AllPlayersGone &&
4531              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4532               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4533               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4534             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4535             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4536         {
4537           int element = Feld[ExitX][ExitY];
4538
4539           Feld[ExitX][ExitY] =
4540             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4541              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4542              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4543              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4544              EL_EM_STEEL_EXIT_CLOSING);
4545
4546           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4547         }
4548
4549         /* player disappears */
4550         DrawLevelField(ExitX, ExitY);
4551       }
4552
4553       for (i = 0; i < MAX_PLAYERS; i++)
4554       {
4555         struct PlayerInfo *player = &stored_player[i];
4556
4557         if (player->present)
4558         {
4559           RemovePlayer(player);
4560
4561           /* player disappears */
4562           DrawLevelField(player->jx, player->jy);
4563         }
4564       }
4565     }
4566
4567     PlaySound(SND_GAME_WINNING);
4568   }
4569
4570   if (game_over_delay_1 > 0)
4571   {
4572     game_over_delay_1--;
4573
4574     return;
4575   }
4576
4577   if (time != time_final)
4578   {
4579     int time_to_go = ABS(time_final - time);
4580     int time_count_dir = (time < time_final ? +1 : -1);
4581
4582     if (time_to_go < time_count_steps)
4583       time_count_steps = 1;
4584
4585     time  += time_count_steps * time_count_dir;
4586     score += time_count_steps * level.score[SC_TIME_BONUS];
4587
4588     local_player->LevelSolved_CountingTime = time;
4589     local_player->LevelSolved_CountingScore = score;
4590
4591     game_panel_controls[GAME_PANEL_TIME].value = time;
4592     game_panel_controls[GAME_PANEL_SCORE].value = score;
4593
4594     DisplayGameControlValues();
4595
4596     if (time == time_final)
4597       StopSound(SND_GAME_LEVELTIME_BONUS);
4598     else if (setup.sound_loops)
4599       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4600     else
4601       PlaySound(SND_GAME_LEVELTIME_BONUS);
4602
4603     return;
4604   }
4605
4606   if (game_over_delay_2 > 0)
4607   {
4608     game_over_delay_2--;
4609
4610     return;
4611   }
4612
4613   if (health != health_final)
4614   {
4615     int health_count_dir = (health < health_final ? +1 : -1);
4616
4617     health += health_count_dir;
4618     score  += level.score[SC_TIME_BONUS];
4619
4620     local_player->LevelSolved_CountingHealth = health;
4621     local_player->LevelSolved_CountingScore = score;
4622
4623     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4624     game_panel_controls[GAME_PANEL_SCORE].value = score;
4625
4626     DisplayGameControlValues();
4627
4628     if (health == health_final)
4629       StopSound(SND_GAME_LEVELTIME_BONUS);
4630     else if (setup.sound_loops)
4631       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4632     else
4633       PlaySound(SND_GAME_LEVELTIME_BONUS);
4634
4635     return;
4636   }
4637
4638   local_player->LevelSolved_PanelOff = TRUE;
4639
4640   if (game_over_delay_3 > 0)
4641   {
4642     game_over_delay_3--;
4643
4644     return;
4645   }
4646
4647   GameEnd();
4648 }
4649
4650 void GameEnd()
4651 {
4652   int hi_pos;
4653   int last_level_nr = level_nr;
4654
4655   local_player->LevelSolved_GameEnd = TRUE;
4656
4657   if (local_player->LevelSolved_SaveTape)
4658   {
4659     /* make sure that request dialog to save tape does not open door again */
4660     if (!global.use_envelope_request)
4661       CloseDoor(DOOR_CLOSE_1);
4662
4663     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4664   }
4665
4666   /* if no tape is to be saved, close both doors simultaneously */
4667   CloseDoor(DOOR_CLOSE_ALL);
4668
4669   if (level_editor_test_game)
4670   {
4671     SetGameStatus(GAME_MODE_MAIN);
4672
4673     DrawMainMenu();
4674
4675     return;
4676   }
4677
4678   if (!local_player->LevelSolved_SaveScore)
4679   {
4680     SetGameStatus(GAME_MODE_MAIN);
4681
4682     DrawMainMenu();
4683
4684     return;
4685   }
4686
4687   if (level_nr == leveldir_current->handicap_level)
4688   {
4689     leveldir_current->handicap_level++;
4690
4691     SaveLevelSetup_SeriesInfo();
4692   }
4693
4694   if (setup.increment_levels &&
4695       level_nr < leveldir_current->last_level)
4696   {
4697     level_nr++;         /* advance to next level */
4698     TapeErase();        /* start with empty tape */
4699   }
4700
4701   hi_pos = NewHiScore(last_level_nr);
4702
4703   if (hi_pos >= 0)
4704   {
4705     SetGameStatus(GAME_MODE_SCORES);
4706
4707     DrawHallOfFame(last_level_nr, hi_pos);
4708   }
4709   else
4710   {
4711     SetGameStatus(GAME_MODE_MAIN);
4712
4713     DrawMainMenu();
4714   }
4715 }
4716
4717 int NewHiScore(int level_nr)
4718 {
4719   int k, l;
4720   int position = -1;
4721   boolean one_score_entry_per_name = !program.many_scores_per_name;
4722
4723   LoadScore(level_nr);
4724
4725   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4726       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4727     return -1;
4728
4729   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4730   {
4731     if (local_player->score_final > highscore[k].Score)
4732     {
4733       /* player has made it to the hall of fame */
4734
4735       if (k < MAX_SCORE_ENTRIES - 1)
4736       {
4737         int m = MAX_SCORE_ENTRIES - 1;
4738
4739         if (one_score_entry_per_name)
4740         {
4741           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4742             if (strEqual(setup.player_name, highscore[l].Name))
4743               m = l;
4744
4745           if (m == k)   /* player's new highscore overwrites his old one */
4746             goto put_into_list;
4747         }
4748
4749         for (l = m; l > k; l--)
4750         {
4751           strcpy(highscore[l].Name, highscore[l - 1].Name);
4752           highscore[l].Score = highscore[l - 1].Score;
4753         }
4754       }
4755
4756       put_into_list:
4757
4758       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4759       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4760       highscore[k].Score = local_player->score_final; 
4761       position = k;
4762
4763       break;
4764     }
4765     else if (one_score_entry_per_name &&
4766              !strncmp(setup.player_name, highscore[k].Name,
4767                       MAX_PLAYER_NAME_LEN))
4768       break;    /* player already there with a higher score */
4769   }
4770
4771   if (position >= 0) 
4772     SaveScore(level_nr);
4773
4774   return position;
4775 }
4776
4777 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4778 {
4779   int element = Feld[x][y];
4780   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4781   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4782   int horiz_move = (dx != 0);
4783   int sign = (horiz_move ? dx : dy);
4784   int step = sign * element_info[element].move_stepsize;
4785
4786   /* special values for move stepsize for spring and things on conveyor belt */
4787   if (horiz_move)
4788   {
4789     if (CAN_FALL(element) &&
4790         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4791       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4792     else if (element == EL_SPRING)
4793       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4794   }
4795
4796   return step;
4797 }
4798
4799 inline static int getElementMoveStepsize(int x, int y)
4800 {
4801   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4802 }
4803
4804 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4805 {
4806   if (player->GfxAction != action || player->GfxDir != dir)
4807   {
4808     player->GfxAction = action;
4809     player->GfxDir = dir;
4810     player->Frame = 0;
4811     player->StepFrame = 0;
4812   }
4813 }
4814
4815 static void ResetGfxFrame(int x, int y)
4816 {
4817   // profiling showed that "autotest" spends 10~20% of its time in this function
4818   if (DrawingDeactivatedField())
4819     return;
4820
4821   int element = Feld[x][y];
4822   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4823
4824   if (graphic_info[graphic].anim_global_sync)
4825     GfxFrame[x][y] = FrameCounter;
4826   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4827     GfxFrame[x][y] = CustomValue[x][y];
4828   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4829     GfxFrame[x][y] = element_info[element].collect_score;
4830   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4831     GfxFrame[x][y] = ChangeDelay[x][y];
4832 }
4833
4834 static void ResetGfxAnimation(int x, int y)
4835 {
4836   GfxAction[x][y] = ACTION_DEFAULT;
4837   GfxDir[x][y] = MovDir[x][y];
4838   GfxFrame[x][y] = 0;
4839
4840   ResetGfxFrame(x, y);
4841 }
4842
4843 static void ResetRandomAnimationValue(int x, int y)
4844 {
4845   GfxRandom[x][y] = INIT_GFX_RANDOM();
4846 }
4847
4848 void InitMovingField(int x, int y, int direction)
4849 {
4850   int element = Feld[x][y];
4851   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4852   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4853   int newx = x + dx;
4854   int newy = y + dy;
4855   boolean is_moving_before, is_moving_after;
4856
4857   /* check if element was/is moving or being moved before/after mode change */
4858   is_moving_before = (WasJustMoving[x][y] != 0);
4859   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4860
4861   /* reset animation only for moving elements which change direction of moving
4862      or which just started or stopped moving
4863      (else CEs with property "can move" / "not moving" are reset each frame) */
4864   if (is_moving_before != is_moving_after ||
4865       direction != MovDir[x][y])
4866     ResetGfxAnimation(x, y);
4867
4868   MovDir[x][y] = direction;
4869   GfxDir[x][y] = direction;
4870
4871   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4872                      direction == MV_DOWN && CAN_FALL(element) ?
4873                      ACTION_FALLING : ACTION_MOVING);
4874
4875   /* this is needed for CEs with property "can move" / "not moving" */
4876
4877   if (is_moving_after)
4878   {
4879     if (Feld[newx][newy] == EL_EMPTY)
4880       Feld[newx][newy] = EL_BLOCKED;
4881
4882     MovDir[newx][newy] = MovDir[x][y];
4883
4884     CustomValue[newx][newy] = CustomValue[x][y];
4885
4886     GfxFrame[newx][newy] = GfxFrame[x][y];
4887     GfxRandom[newx][newy] = GfxRandom[x][y];
4888     GfxAction[newx][newy] = GfxAction[x][y];
4889     GfxDir[newx][newy] = GfxDir[x][y];
4890   }
4891 }
4892
4893 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4894 {
4895   int direction = MovDir[x][y];
4896   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4897   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4898
4899   *goes_to_x = newx;
4900   *goes_to_y = newy;
4901 }
4902
4903 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4904 {
4905   int oldx = x, oldy = y;
4906   int direction = MovDir[x][y];
4907
4908   if (direction == MV_LEFT)
4909     oldx++;
4910   else if (direction == MV_RIGHT)
4911     oldx--;
4912   else if (direction == MV_UP)
4913     oldy++;
4914   else if (direction == MV_DOWN)
4915     oldy--;
4916
4917   *comes_from_x = oldx;
4918   *comes_from_y = oldy;
4919 }
4920
4921 int MovingOrBlocked2Element(int x, int y)
4922 {
4923   int element = Feld[x][y];
4924
4925   if (element == EL_BLOCKED)
4926   {
4927     int oldx, oldy;
4928
4929     Blocked2Moving(x, y, &oldx, &oldy);
4930     return Feld[oldx][oldy];
4931   }
4932   else
4933     return element;
4934 }
4935
4936 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4937 {
4938   /* like MovingOrBlocked2Element(), but if element is moving
4939      and (x,y) is the field the moving element is just leaving,
4940      return EL_BLOCKED instead of the element value */
4941   int element = Feld[x][y];
4942
4943   if (IS_MOVING(x, y))
4944   {
4945     if (element == EL_BLOCKED)
4946     {
4947       int oldx, oldy;
4948
4949       Blocked2Moving(x, y, &oldx, &oldy);
4950       return Feld[oldx][oldy];
4951     }
4952     else
4953       return EL_BLOCKED;
4954   }
4955   else
4956     return element;
4957 }
4958
4959 static void RemoveField(int x, int y)
4960 {
4961   Feld[x][y] = EL_EMPTY;
4962
4963   MovPos[x][y] = 0;
4964   MovDir[x][y] = 0;
4965   MovDelay[x][y] = 0;
4966
4967   CustomValue[x][y] = 0;
4968
4969   AmoebaNr[x][y] = 0;
4970   ChangeDelay[x][y] = 0;
4971   ChangePage[x][y] = -1;
4972   Pushed[x][y] = FALSE;
4973
4974   GfxElement[x][y] = EL_UNDEFINED;
4975   GfxAction[x][y] = ACTION_DEFAULT;
4976   GfxDir[x][y] = MV_NONE;
4977 }
4978
4979 void RemoveMovingField(int x, int y)
4980 {
4981   int oldx = x, oldy = y, newx = x, newy = y;
4982   int element = Feld[x][y];
4983   int next_element = EL_UNDEFINED;
4984
4985   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4986     return;
4987
4988   if (IS_MOVING(x, y))
4989   {
4990     Moving2Blocked(x, y, &newx, &newy);
4991
4992     if (Feld[newx][newy] != EL_BLOCKED)
4993     {
4994       /* element is moving, but target field is not free (blocked), but
4995          already occupied by something different (example: acid pool);
4996          in this case, only remove the moving field, but not the target */
4997
4998       RemoveField(oldx, oldy);
4999
5000       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5001
5002       TEST_DrawLevelField(oldx, oldy);
5003
5004       return;
5005     }
5006   }
5007   else if (element == EL_BLOCKED)
5008   {
5009     Blocked2Moving(x, y, &oldx, &oldy);
5010     if (!IS_MOVING(oldx, oldy))
5011       return;
5012   }
5013
5014   if (element == EL_BLOCKED &&
5015       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5016        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5017        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5018        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5019        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5020        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5021     next_element = get_next_element(Feld[oldx][oldy]);
5022
5023   RemoveField(oldx, oldy);
5024   RemoveField(newx, newy);
5025
5026   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5027
5028   if (next_element != EL_UNDEFINED)
5029     Feld[oldx][oldy] = next_element;
5030
5031   TEST_DrawLevelField(oldx, oldy);
5032   TEST_DrawLevelField(newx, newy);
5033 }
5034
5035 void DrawDynamite(int x, int y)
5036 {
5037   int sx = SCREENX(x), sy = SCREENY(y);
5038   int graphic = el2img(Feld[x][y]);
5039   int frame;
5040
5041   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5042     return;
5043
5044   if (IS_WALKABLE_INSIDE(Back[x][y]))
5045     return;
5046
5047   if (Back[x][y])
5048     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5049   else if (Store[x][y])
5050     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5051
5052   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5053
5054   if (Back[x][y] || Store[x][y])
5055     DrawGraphicThruMask(sx, sy, graphic, frame);
5056   else
5057     DrawGraphic(sx, sy, graphic, frame);
5058 }
5059
5060 void CheckDynamite(int x, int y)
5061 {
5062   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5063   {
5064     MovDelay[x][y]--;
5065
5066     if (MovDelay[x][y] != 0)
5067     {
5068       DrawDynamite(x, y);
5069       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5070
5071       return;
5072     }
5073   }
5074
5075   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5076
5077   Bang(x, y);
5078 }
5079
5080 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5081 {
5082   boolean num_checked_players = 0;
5083   int i;
5084
5085   for (i = 0; i < MAX_PLAYERS; i++)
5086   {
5087     if (stored_player[i].active)
5088     {
5089       int sx = stored_player[i].jx;
5090       int sy = stored_player[i].jy;
5091
5092       if (num_checked_players == 0)
5093       {
5094         *sx1 = *sx2 = sx;
5095         *sy1 = *sy2 = sy;
5096       }
5097       else
5098       {
5099         *sx1 = MIN(*sx1, sx);
5100         *sy1 = MIN(*sy1, sy);
5101         *sx2 = MAX(*sx2, sx);
5102         *sy2 = MAX(*sy2, sy);
5103       }
5104
5105       num_checked_players++;
5106     }
5107   }
5108 }
5109
5110 static boolean checkIfAllPlayersFitToScreen_RND()
5111 {
5112   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5113
5114   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5115
5116   return (sx2 - sx1 < SCR_FIELDX &&
5117           sy2 - sy1 < SCR_FIELDY);
5118 }
5119
5120 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5121 {
5122   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5123
5124   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5125
5126   *sx = (sx1 + sx2) / 2;
5127   *sy = (sy1 + sy2) / 2;
5128 }
5129
5130 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5131                         boolean center_screen, boolean quick_relocation)
5132 {
5133   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5134   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5135   boolean no_delay = (tape.warp_forward);
5136   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5137   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5138   int new_scroll_x, new_scroll_y;
5139
5140   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5141   {
5142     /* case 1: quick relocation inside visible screen (without scrolling) */
5143
5144     RedrawPlayfield();
5145
5146     return;
5147   }
5148
5149   if (!level.shifted_relocation || center_screen)
5150   {
5151     /* relocation _with_ centering of screen */
5152
5153     new_scroll_x = SCROLL_POSITION_X(x);
5154     new_scroll_y = SCROLL_POSITION_Y(y);
5155   }
5156   else
5157   {
5158     /* relocation _without_ centering of screen */
5159
5160     int center_scroll_x = SCROLL_POSITION_X(old_x);
5161     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5162     int offset_x = x + (scroll_x - center_scroll_x);
5163     int offset_y = y + (scroll_y - center_scroll_y);
5164
5165     /* for new screen position, apply previous offset to center position */
5166     new_scroll_x = SCROLL_POSITION_X(offset_x);
5167     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5168   }
5169
5170   if (quick_relocation)
5171   {
5172     /* case 2: quick relocation (redraw without visible scrolling) */
5173
5174     scroll_x = new_scroll_x;
5175     scroll_y = new_scroll_y;
5176
5177     RedrawPlayfield();
5178
5179     return;
5180   }
5181
5182   /* case 3: visible relocation (with scrolling to new position) */
5183
5184   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5185
5186   SetVideoFrameDelay(wait_delay_value);
5187
5188   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5189   {
5190     int dx = 0, dy = 0;
5191     int fx = FX, fy = FY;
5192
5193     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5194     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5195
5196     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5197       break;
5198
5199     scroll_x -= dx;
5200     scroll_y -= dy;
5201
5202     fx += dx * TILEX / 2;
5203     fy += dy * TILEY / 2;
5204
5205     ScrollLevel(dx, dy);
5206     DrawAllPlayers();
5207
5208     /* scroll in two steps of half tile size to make things smoother */
5209     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5210
5211     /* scroll second step to align at full tile size */
5212     BlitScreenToBitmap(window);
5213   }
5214
5215   DrawAllPlayers();
5216   BackToFront();
5217
5218   SetVideoFrameDelay(frame_delay_value_old);
5219 }
5220
5221 void RelocatePlayer(int jx, int jy, int el_player_raw)
5222 {
5223   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5224   int player_nr = GET_PLAYER_NR(el_player);
5225   struct PlayerInfo *player = &stored_player[player_nr];
5226   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5227   boolean no_delay = (tape.warp_forward);
5228   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5229   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5230   int old_jx = player->jx;
5231   int old_jy = player->jy;
5232   int old_element = Feld[old_jx][old_jy];
5233   int element = Feld[jx][jy];
5234   boolean player_relocated = (old_jx != jx || old_jy != jy);
5235
5236   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5237   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5238   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5239   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5240   int leave_side_horiz = move_dir_horiz;
5241   int leave_side_vert  = move_dir_vert;
5242   int enter_side = enter_side_horiz | enter_side_vert;
5243   int leave_side = leave_side_horiz | leave_side_vert;
5244
5245   if (player->GameOver)         /* do not reanimate dead player */
5246     return;
5247
5248   if (!player_relocated)        /* no need to relocate the player */
5249     return;
5250
5251   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5252   {
5253     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5254     DrawLevelField(jx, jy);
5255   }
5256
5257   if (player->present)
5258   {
5259     while (player->MovPos)
5260     {
5261       ScrollPlayer(player, SCROLL_GO_ON);
5262       ScrollScreen(NULL, SCROLL_GO_ON);
5263
5264       AdvanceFrameAndPlayerCounters(player->index_nr);
5265
5266       DrawPlayer(player);
5267
5268       BackToFront_WithFrameDelay(wait_delay_value);
5269     }
5270
5271     DrawPlayer(player);         /* needed here only to cleanup last field */
5272     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5273
5274     player->is_moving = FALSE;
5275   }
5276
5277   if (IS_CUSTOM_ELEMENT(old_element))
5278     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5279                                CE_LEFT_BY_PLAYER,
5280                                player->index_bit, leave_side);
5281
5282   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5283                                       CE_PLAYER_LEAVES_X,
5284                                       player->index_bit, leave_side);
5285
5286   Feld[jx][jy] = el_player;
5287   InitPlayerField(jx, jy, el_player, TRUE);
5288
5289   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5290      possible that the relocation target field did not contain a player element,
5291      but a walkable element, to which the new player was relocated -- in this
5292      case, restore that (already initialized!) element on the player field */
5293   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5294   {
5295     Feld[jx][jy] = element;     /* restore previously existing element */
5296   }
5297
5298   /* only visually relocate centered player */
5299   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5300                      FALSE, level.instant_relocation);
5301
5302   TestIfPlayerTouchesBadThing(jx, jy);
5303   TestIfPlayerTouchesCustomElement(jx, jy);
5304
5305   if (IS_CUSTOM_ELEMENT(element))
5306     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5307                                player->index_bit, enter_side);
5308
5309   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5310                                       player->index_bit, enter_side);
5311
5312   if (player->is_switching)
5313   {
5314     /* ensure that relocation while still switching an element does not cause
5315        a new element to be treated as also switched directly after relocation
5316        (this is important for teleporter switches that teleport the player to
5317        a place where another teleporter switch is in the same direction, which
5318        would then incorrectly be treated as immediately switched before the
5319        direction key that caused the switch was released) */
5320
5321     player->switch_x += jx - old_jx;
5322     player->switch_y += jy - old_jy;
5323   }
5324 }
5325
5326 void Explode(int ex, int ey, int phase, int mode)
5327 {
5328   int x, y;
5329   int last_phase;
5330   int border_element;
5331
5332   /* !!! eliminate this variable !!! */
5333   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5334
5335   if (game.explosions_delayed)
5336   {
5337     ExplodeField[ex][ey] = mode;
5338     return;
5339   }
5340
5341   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5342   {
5343     int center_element = Feld[ex][ey];
5344     int artwork_element, explosion_element;     /* set these values later */
5345
5346     /* remove things displayed in background while burning dynamite */
5347     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5348       Back[ex][ey] = 0;
5349
5350     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5351     {
5352       /* put moving element to center field (and let it explode there) */
5353       center_element = MovingOrBlocked2Element(ex, ey);
5354       RemoveMovingField(ex, ey);
5355       Feld[ex][ey] = center_element;
5356     }
5357
5358     /* now "center_element" is finally determined -- set related values now */
5359     artwork_element = center_element;           /* for custom player artwork */
5360     explosion_element = center_element;         /* for custom player artwork */
5361
5362     if (IS_PLAYER(ex, ey))
5363     {
5364       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5365
5366       artwork_element = stored_player[player_nr].artwork_element;
5367
5368       if (level.use_explosion_element[player_nr])
5369       {
5370         explosion_element = level.explosion_element[player_nr];
5371         artwork_element = explosion_element;
5372       }
5373     }
5374
5375     if (mode == EX_TYPE_NORMAL ||
5376         mode == EX_TYPE_CENTER ||
5377         mode == EX_TYPE_CROSS)
5378       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5379
5380     last_phase = element_info[explosion_element].explosion_delay + 1;
5381
5382     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5383     {
5384       int xx = x - ex + 1;
5385       int yy = y - ey + 1;
5386       int element;
5387
5388       if (!IN_LEV_FIELD(x, y) ||
5389           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5390           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5391         continue;
5392
5393       element = Feld[x][y];
5394
5395       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5396       {
5397         element = MovingOrBlocked2Element(x, y);
5398
5399         if (!IS_EXPLOSION_PROOF(element))
5400           RemoveMovingField(x, y);
5401       }
5402
5403       /* indestructible elements can only explode in center (but not flames) */
5404       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5405                                            mode == EX_TYPE_BORDER)) ||
5406           element == EL_FLAMES)
5407         continue;
5408
5409       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5410          behaviour, for example when touching a yamyam that explodes to rocks
5411          with active deadly shield, a rock is created under the player !!! */
5412       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5413 #if 0
5414       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5415           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5416            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5417 #else
5418       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5419 #endif
5420       {
5421         if (IS_ACTIVE_BOMB(element))
5422         {
5423           /* re-activate things under the bomb like gate or penguin */
5424           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5425           Back[x][y] = 0;
5426         }
5427
5428         continue;
5429       }
5430
5431       /* save walkable background elements while explosion on same tile */
5432       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5433           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5434         Back[x][y] = element;
5435
5436       /* ignite explodable elements reached by other explosion */
5437       if (element == EL_EXPLOSION)
5438         element = Store2[x][y];
5439
5440       if (AmoebaNr[x][y] &&
5441           (element == EL_AMOEBA_FULL ||
5442            element == EL_BD_AMOEBA ||
5443            element == EL_AMOEBA_GROWING))
5444       {
5445         AmoebaCnt[AmoebaNr[x][y]]--;
5446         AmoebaCnt2[AmoebaNr[x][y]]--;
5447       }
5448
5449       RemoveField(x, y);
5450
5451       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5452       {
5453         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5454
5455         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5456
5457         if (PLAYERINFO(ex, ey)->use_murphy)
5458           Store[x][y] = EL_EMPTY;
5459       }
5460
5461       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5462          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5463       else if (ELEM_IS_PLAYER(center_element))
5464         Store[x][y] = EL_EMPTY;
5465       else if (center_element == EL_YAMYAM)
5466         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5467       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5468         Store[x][y] = element_info[center_element].content.e[xx][yy];
5469 #if 1
5470       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5471          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5472          otherwise) -- FIX THIS !!! */
5473       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5474         Store[x][y] = element_info[element].content.e[1][1];
5475 #else
5476       else if (!CAN_EXPLODE(element))
5477         Store[x][y] = element_info[element].content.e[1][1];
5478 #endif
5479       else
5480         Store[x][y] = EL_EMPTY;
5481
5482       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5483           center_element == EL_AMOEBA_TO_DIAMOND)
5484         Store2[x][y] = element;
5485
5486       Feld[x][y] = EL_EXPLOSION;
5487       GfxElement[x][y] = artwork_element;
5488
5489       ExplodePhase[x][y] = 1;
5490       ExplodeDelay[x][y] = last_phase;
5491
5492       Stop[x][y] = TRUE;
5493     }
5494
5495     if (center_element == EL_YAMYAM)
5496       game.yamyam_content_nr =
5497         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5498
5499     return;
5500   }
5501
5502   if (Stop[ex][ey])
5503     return;
5504
5505   x = ex;
5506   y = ey;
5507
5508   if (phase == 1)
5509     GfxFrame[x][y] = 0;         /* restart explosion animation */
5510
5511   last_phase = ExplodeDelay[x][y];
5512
5513   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5514
5515   /* this can happen if the player leaves an explosion just in time */
5516   if (GfxElement[x][y] == EL_UNDEFINED)
5517     GfxElement[x][y] = EL_EMPTY;
5518
5519   border_element = Store2[x][y];
5520   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5521     border_element = StorePlayer[x][y];
5522
5523   if (phase == element_info[border_element].ignition_delay ||
5524       phase == last_phase)
5525   {
5526     boolean border_explosion = FALSE;
5527
5528     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5529         !PLAYER_EXPLOSION_PROTECTED(x, y))
5530     {
5531       KillPlayerUnlessExplosionProtected(x, y);
5532       border_explosion = TRUE;
5533     }
5534     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5535     {
5536       Feld[x][y] = Store2[x][y];
5537       Store2[x][y] = 0;
5538       Bang(x, y);
5539       border_explosion = TRUE;
5540     }
5541     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5542     {
5543       AmoebeUmwandeln(x, y);
5544       Store2[x][y] = 0;
5545       border_explosion = TRUE;
5546     }
5547
5548     /* if an element just explodes due to another explosion (chain-reaction),
5549        do not immediately end the new explosion when it was the last frame of
5550        the explosion (as it would be done in the following "if"-statement!) */
5551     if (border_explosion && phase == last_phase)
5552       return;
5553   }
5554
5555   if (phase == last_phase)
5556   {
5557     int element;
5558
5559     element = Feld[x][y] = Store[x][y];
5560     Store[x][y] = Store2[x][y] = 0;
5561     GfxElement[x][y] = EL_UNDEFINED;
5562
5563     /* player can escape from explosions and might therefore be still alive */
5564     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5565         element <= EL_PLAYER_IS_EXPLODING_4)
5566     {
5567       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5568       int explosion_element = EL_PLAYER_1 + player_nr;
5569       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5570       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5571
5572       if (level.use_explosion_element[player_nr])
5573         explosion_element = level.explosion_element[player_nr];
5574
5575       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5576                     element_info[explosion_element].content.e[xx][yy]);
5577     }
5578
5579     /* restore probably existing indestructible background element */
5580     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5581       element = Feld[x][y] = Back[x][y];
5582     Back[x][y] = 0;
5583
5584     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5585     GfxDir[x][y] = MV_NONE;
5586     ChangeDelay[x][y] = 0;
5587     ChangePage[x][y] = -1;
5588
5589     CustomValue[x][y] = 0;
5590
5591     InitField_WithBug2(x, y, FALSE);
5592
5593     TEST_DrawLevelField(x, y);
5594
5595     TestIfElementTouchesCustomElement(x, y);
5596
5597     if (GFX_CRUMBLED(element))
5598       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5599
5600     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5601       StorePlayer[x][y] = 0;
5602
5603     if (ELEM_IS_PLAYER(element))
5604       RelocatePlayer(x, y, element);
5605   }
5606   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5607   {
5608     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5609     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5610
5611     if (phase == delay)
5612       TEST_DrawLevelFieldCrumbled(x, y);
5613
5614     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5615     {
5616       DrawLevelElement(x, y, Back[x][y]);
5617       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5618     }
5619     else if (IS_WALKABLE_UNDER(Back[x][y]))
5620     {
5621       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5622       DrawLevelElementThruMask(x, y, Back[x][y]);
5623     }
5624     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5625       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5626   }
5627 }
5628
5629 void DynaExplode(int ex, int ey)
5630 {
5631   int i, j;
5632   int dynabomb_element = Feld[ex][ey];
5633   int dynabomb_size = 1;
5634   boolean dynabomb_xl = FALSE;
5635   struct PlayerInfo *player;
5636   static int xy[4][2] =
5637   {
5638     { 0, -1 },
5639     { -1, 0 },
5640     { +1, 0 },
5641     { 0, +1 }
5642   };
5643
5644   if (IS_ACTIVE_BOMB(dynabomb_element))
5645   {
5646     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5647     dynabomb_size = player->dynabomb_size;
5648     dynabomb_xl = player->dynabomb_xl;
5649     player->dynabombs_left++;
5650   }
5651
5652   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5653
5654   for (i = 0; i < NUM_DIRECTIONS; i++)
5655   {
5656     for (j = 1; j <= dynabomb_size; j++)
5657     {
5658       int x = ex + j * xy[i][0];
5659       int y = ey + j * xy[i][1];
5660       int element;
5661
5662       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5663         break;
5664
5665       element = Feld[x][y];
5666
5667       /* do not restart explosions of fields with active bombs */
5668       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5669         continue;
5670
5671       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5672
5673       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5674           !IS_DIGGABLE(element) && !dynabomb_xl)
5675         break;
5676     }
5677   }
5678 }
5679
5680 void Bang(int x, int y)
5681 {
5682   int element = MovingOrBlocked2Element(x, y);
5683   int explosion_type = EX_TYPE_NORMAL;
5684
5685   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5686   {
5687     struct PlayerInfo *player = PLAYERINFO(x, y);
5688
5689     element = Feld[x][y] = player->initial_element;
5690
5691     if (level.use_explosion_element[player->index_nr])
5692     {
5693       int explosion_element = level.explosion_element[player->index_nr];
5694
5695       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5696         explosion_type = EX_TYPE_CROSS;
5697       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5698         explosion_type = EX_TYPE_CENTER;
5699     }
5700   }
5701
5702   switch (element)
5703   {
5704     case EL_BUG:
5705     case EL_SPACESHIP:
5706     case EL_BD_BUTTERFLY:
5707     case EL_BD_FIREFLY:
5708     case EL_YAMYAM:
5709     case EL_DARK_YAMYAM:
5710     case EL_ROBOT:
5711     case EL_PACMAN:
5712     case EL_MOLE:
5713       RaiseScoreElement(element);
5714       break;
5715
5716     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5717     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5718     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5719     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5720     case EL_DYNABOMB_INCREASE_NUMBER:
5721     case EL_DYNABOMB_INCREASE_SIZE:
5722     case EL_DYNABOMB_INCREASE_POWER:
5723       explosion_type = EX_TYPE_DYNA;
5724       break;
5725
5726     case EL_DC_LANDMINE:
5727       explosion_type = EX_TYPE_CENTER;
5728       break;
5729
5730     case EL_PENGUIN:
5731     case EL_LAMP:
5732     case EL_LAMP_ACTIVE:
5733     case EL_AMOEBA_TO_DIAMOND:
5734       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5735         explosion_type = EX_TYPE_CENTER;
5736       break;
5737
5738     default:
5739       if (element_info[element].explosion_type == EXPLODES_CROSS)
5740         explosion_type = EX_TYPE_CROSS;
5741       else if (element_info[element].explosion_type == EXPLODES_1X1)
5742         explosion_type = EX_TYPE_CENTER;
5743       break;
5744   }
5745
5746   if (explosion_type == EX_TYPE_DYNA)
5747     DynaExplode(x, y);
5748   else
5749     Explode(x, y, EX_PHASE_START, explosion_type);
5750
5751   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5752 }
5753
5754 void SplashAcid(int x, int y)
5755 {
5756   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5757       (!IN_LEV_FIELD(x - 1, y - 2) ||
5758        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5759     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5760
5761   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5762       (!IN_LEV_FIELD(x + 1, y - 2) ||
5763        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5764     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5765
5766   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5767 }
5768
5769 static void InitBeltMovement()
5770 {
5771   static int belt_base_element[4] =
5772   {
5773     EL_CONVEYOR_BELT_1_LEFT,
5774     EL_CONVEYOR_BELT_2_LEFT,
5775     EL_CONVEYOR_BELT_3_LEFT,
5776     EL_CONVEYOR_BELT_4_LEFT
5777   };
5778   static int belt_base_active_element[4] =
5779   {
5780     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5781     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5782     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5783     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5784   };
5785
5786   int x, y, i, j;
5787
5788   /* set frame order for belt animation graphic according to belt direction */
5789   for (i = 0; i < NUM_BELTS; i++)
5790   {
5791     int belt_nr = i;
5792
5793     for (j = 0; j < NUM_BELT_PARTS; j++)
5794     {
5795       int element = belt_base_active_element[belt_nr] + j;
5796       int graphic_1 = el2img(element);
5797       int graphic_2 = el2panelimg(element);
5798
5799       if (game.belt_dir[i] == MV_LEFT)
5800       {
5801         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5802         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5803       }
5804       else
5805       {
5806         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5807         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5808       }
5809     }
5810   }
5811
5812   SCAN_PLAYFIELD(x, y)
5813   {
5814     int element = Feld[x][y];
5815
5816     for (i = 0; i < NUM_BELTS; i++)
5817     {
5818       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5819       {
5820         int e_belt_nr = getBeltNrFromBeltElement(element);
5821         int belt_nr = i;
5822
5823         if (e_belt_nr == belt_nr)
5824         {
5825           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5826
5827           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5828         }
5829       }
5830     }
5831   }
5832 }
5833
5834 static void ToggleBeltSwitch(int x, int y)
5835 {
5836   static int belt_base_element[4] =
5837   {
5838     EL_CONVEYOR_BELT_1_LEFT,
5839     EL_CONVEYOR_BELT_2_LEFT,
5840     EL_CONVEYOR_BELT_3_LEFT,
5841     EL_CONVEYOR_BELT_4_LEFT
5842   };
5843   static int belt_base_active_element[4] =
5844   {
5845     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5846     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5847     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5848     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5849   };
5850   static int belt_base_switch_element[4] =
5851   {
5852     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5853     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5854     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5855     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5856   };
5857   static int belt_move_dir[4] =
5858   {
5859     MV_LEFT,
5860     MV_NONE,
5861     MV_RIGHT,
5862     MV_NONE,
5863   };
5864
5865   int element = Feld[x][y];
5866   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5867   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5868   int belt_dir = belt_move_dir[belt_dir_nr];
5869   int xx, yy, i;
5870
5871   if (!IS_BELT_SWITCH(element))
5872     return;
5873
5874   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5875   game.belt_dir[belt_nr] = belt_dir;
5876
5877   if (belt_dir_nr == 3)
5878     belt_dir_nr = 1;
5879
5880   /* set frame order for belt animation graphic according to belt direction */
5881   for (i = 0; i < NUM_BELT_PARTS; i++)
5882   {
5883     int element = belt_base_active_element[belt_nr] + i;
5884     int graphic_1 = el2img(element);
5885     int graphic_2 = el2panelimg(element);
5886
5887     if (belt_dir == MV_LEFT)
5888     {
5889       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5890       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5891     }
5892     else
5893     {
5894       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5895       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5896     }
5897   }
5898
5899   SCAN_PLAYFIELD(xx, yy)
5900   {
5901     int element = Feld[xx][yy];
5902
5903     if (IS_BELT_SWITCH(element))
5904     {
5905       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5906
5907       if (e_belt_nr == belt_nr)
5908       {
5909         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5910         TEST_DrawLevelField(xx, yy);
5911       }
5912     }
5913     else if (IS_BELT(element) && belt_dir != MV_NONE)
5914     {
5915       int e_belt_nr = getBeltNrFromBeltElement(element);
5916
5917       if (e_belt_nr == belt_nr)
5918       {
5919         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5920
5921         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5922         TEST_DrawLevelField(xx, yy);
5923       }
5924     }
5925     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5926     {
5927       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5928
5929       if (e_belt_nr == belt_nr)
5930       {
5931         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5932
5933         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5934         TEST_DrawLevelField(xx, yy);
5935       }
5936     }
5937   }
5938 }
5939
5940 static void ToggleSwitchgateSwitch(int x, int y)
5941 {
5942   int xx, yy;
5943
5944   game.switchgate_pos = !game.switchgate_pos;
5945
5946   SCAN_PLAYFIELD(xx, yy)
5947   {
5948     int element = Feld[xx][yy];
5949
5950     if (element == EL_SWITCHGATE_SWITCH_UP)
5951     {
5952       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5953       TEST_DrawLevelField(xx, yy);
5954     }
5955     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5956     {
5957       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5958       TEST_DrawLevelField(xx, yy);
5959     }
5960     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5961     {
5962       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5963       TEST_DrawLevelField(xx, yy);
5964     }
5965     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5966     {
5967       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5968       TEST_DrawLevelField(xx, yy);
5969     }
5970     else if (element == EL_SWITCHGATE_OPEN ||
5971              element == EL_SWITCHGATE_OPENING)
5972     {
5973       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5974
5975       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5976     }
5977     else if (element == EL_SWITCHGATE_CLOSED ||
5978              element == EL_SWITCHGATE_CLOSING)
5979     {
5980       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5981
5982       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5983     }
5984   }
5985 }
5986
5987 static int getInvisibleActiveFromInvisibleElement(int element)
5988 {
5989   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5990           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5991           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5992           element);
5993 }
5994
5995 static int getInvisibleFromInvisibleActiveElement(int element)
5996 {
5997   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5998           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5999           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6000           element);
6001 }
6002
6003 static void RedrawAllLightSwitchesAndInvisibleElements()
6004 {
6005   int x, y;
6006
6007   SCAN_PLAYFIELD(x, y)
6008   {
6009     int element = Feld[x][y];
6010
6011     if (element == EL_LIGHT_SWITCH &&
6012         game.light_time_left > 0)
6013     {
6014       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6015       TEST_DrawLevelField(x, y);
6016     }
6017     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6018              game.light_time_left == 0)
6019     {
6020       Feld[x][y] = EL_LIGHT_SWITCH;
6021       TEST_DrawLevelField(x, y);
6022     }
6023     else if (element == EL_EMC_DRIPPER &&
6024              game.light_time_left > 0)
6025     {
6026       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6027       TEST_DrawLevelField(x, y);
6028     }
6029     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6030              game.light_time_left == 0)
6031     {
6032       Feld[x][y] = EL_EMC_DRIPPER;
6033       TEST_DrawLevelField(x, y);
6034     }
6035     else if (element == EL_INVISIBLE_STEELWALL ||
6036              element == EL_INVISIBLE_WALL ||
6037              element == EL_INVISIBLE_SAND)
6038     {
6039       if (game.light_time_left > 0)
6040         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6041
6042       TEST_DrawLevelField(x, y);
6043
6044       /* uncrumble neighbour fields, if needed */
6045       if (element == EL_INVISIBLE_SAND)
6046         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6047     }
6048     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6049              element == EL_INVISIBLE_WALL_ACTIVE ||
6050              element == EL_INVISIBLE_SAND_ACTIVE)
6051     {
6052       if (game.light_time_left == 0)
6053         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6054
6055       TEST_DrawLevelField(x, y);
6056
6057       /* re-crumble neighbour fields, if needed */
6058       if (element == EL_INVISIBLE_SAND)
6059         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6060     }
6061   }
6062 }
6063
6064 static void RedrawAllInvisibleElementsForLenses()
6065 {
6066   int x, y;
6067
6068   SCAN_PLAYFIELD(x, y)
6069   {
6070     int element = Feld[x][y];
6071
6072     if (element == EL_EMC_DRIPPER &&
6073         game.lenses_time_left > 0)
6074     {
6075       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6076       TEST_DrawLevelField(x, y);
6077     }
6078     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6079              game.lenses_time_left == 0)
6080     {
6081       Feld[x][y] = EL_EMC_DRIPPER;
6082       TEST_DrawLevelField(x, y);
6083     }
6084     else if (element == EL_INVISIBLE_STEELWALL ||
6085              element == EL_INVISIBLE_WALL ||
6086              element == EL_INVISIBLE_SAND)
6087     {
6088       if (game.lenses_time_left > 0)
6089         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6090
6091       TEST_DrawLevelField(x, y);
6092
6093       /* uncrumble neighbour fields, if needed */
6094       if (element == EL_INVISIBLE_SAND)
6095         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6096     }
6097     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6098              element == EL_INVISIBLE_WALL_ACTIVE ||
6099              element == EL_INVISIBLE_SAND_ACTIVE)
6100     {
6101       if (game.lenses_time_left == 0)
6102         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6103
6104       TEST_DrawLevelField(x, y);
6105
6106       /* re-crumble neighbour fields, if needed */
6107       if (element == EL_INVISIBLE_SAND)
6108         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6109     }
6110   }
6111 }
6112
6113 static void RedrawAllInvisibleElementsForMagnifier()
6114 {
6115   int x, y;
6116
6117   SCAN_PLAYFIELD(x, y)
6118   {
6119     int element = Feld[x][y];
6120
6121     if (element == EL_EMC_FAKE_GRASS &&
6122         game.magnify_time_left > 0)
6123     {
6124       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6125       TEST_DrawLevelField(x, y);
6126     }
6127     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6128              game.magnify_time_left == 0)
6129     {
6130       Feld[x][y] = EL_EMC_FAKE_GRASS;
6131       TEST_DrawLevelField(x, y);
6132     }
6133     else if (IS_GATE_GRAY(element) &&
6134              game.magnify_time_left > 0)
6135     {
6136       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6137                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6138                     IS_EM_GATE_GRAY(element) ?
6139                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6140                     IS_EMC_GATE_GRAY(element) ?
6141                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6142                     IS_DC_GATE_GRAY(element) ?
6143                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6144                     element);
6145       TEST_DrawLevelField(x, y);
6146     }
6147     else if (IS_GATE_GRAY_ACTIVE(element) &&
6148              game.magnify_time_left == 0)
6149     {
6150       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6151                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6152                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6153                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6154                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6155                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6156                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6157                     EL_DC_GATE_WHITE_GRAY :
6158                     element);
6159       TEST_DrawLevelField(x, y);
6160     }
6161   }
6162 }
6163
6164 static void ToggleLightSwitch(int x, int y)
6165 {
6166   int element = Feld[x][y];
6167
6168   game.light_time_left =
6169     (element == EL_LIGHT_SWITCH ?
6170      level.time_light * FRAMES_PER_SECOND : 0);
6171
6172   RedrawAllLightSwitchesAndInvisibleElements();
6173 }
6174
6175 static void ActivateTimegateSwitch(int x, int y)
6176 {
6177   int xx, yy;
6178
6179   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6180
6181   SCAN_PLAYFIELD(xx, yy)
6182   {
6183     int element = Feld[xx][yy];
6184
6185     if (element == EL_TIMEGATE_CLOSED ||
6186         element == EL_TIMEGATE_CLOSING)
6187     {
6188       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6189       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6190     }
6191
6192     /*
6193     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6194     {
6195       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6196       TEST_DrawLevelField(xx, yy);
6197     }
6198     */
6199
6200   }
6201
6202   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6203                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6204 }
6205
6206 void Impact(int x, int y)
6207 {
6208   boolean last_line = (y == lev_fieldy - 1);
6209   boolean object_hit = FALSE;
6210   boolean impact = (last_line || object_hit);
6211   int element = Feld[x][y];
6212   int smashed = EL_STEELWALL;
6213
6214   if (!last_line)       /* check if element below was hit */
6215   {
6216     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6217       return;
6218
6219     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6220                                          MovDir[x][y + 1] != MV_DOWN ||
6221                                          MovPos[x][y + 1] <= TILEY / 2));
6222
6223     /* do not smash moving elements that left the smashed field in time */
6224     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6225         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6226       object_hit = FALSE;
6227
6228 #if USE_QUICKSAND_IMPACT_BUGFIX
6229     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6230     {
6231       RemoveMovingField(x, y + 1);
6232       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6233       Feld[x][y + 2] = EL_ROCK;
6234       TEST_DrawLevelField(x, y + 2);
6235
6236       object_hit = TRUE;
6237     }
6238
6239     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6240     {
6241       RemoveMovingField(x, y + 1);
6242       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6243       Feld[x][y + 2] = EL_ROCK;
6244       TEST_DrawLevelField(x, y + 2);
6245
6246       object_hit = TRUE;
6247     }
6248 #endif
6249
6250     if (object_hit)
6251       smashed = MovingOrBlocked2Element(x, y + 1);
6252
6253     impact = (last_line || object_hit);
6254   }
6255
6256   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6257   {
6258     SplashAcid(x, y + 1);
6259     return;
6260   }
6261
6262   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6263   /* only reset graphic animation if graphic really changes after impact */
6264   if (impact &&
6265       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6266   {
6267     ResetGfxAnimation(x, y);
6268     TEST_DrawLevelField(x, y);
6269   }
6270
6271   if (impact && CAN_EXPLODE_IMPACT(element))
6272   {
6273     Bang(x, y);
6274     return;
6275   }
6276   else if (impact && element == EL_PEARL &&
6277            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6278   {
6279     ResetGfxAnimation(x, y);
6280
6281     Feld[x][y] = EL_PEARL_BREAKING;
6282     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6283     return;
6284   }
6285   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6286   {
6287     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6288
6289     return;
6290   }
6291
6292   if (impact && element == EL_AMOEBA_DROP)
6293   {
6294     if (object_hit && IS_PLAYER(x, y + 1))
6295       KillPlayerUnlessEnemyProtected(x, y + 1);
6296     else if (object_hit && smashed == EL_PENGUIN)
6297       Bang(x, y + 1);
6298     else
6299     {
6300       Feld[x][y] = EL_AMOEBA_GROWING;
6301       Store[x][y] = EL_AMOEBA_WET;
6302
6303       ResetRandomAnimationValue(x, y);
6304     }
6305     return;
6306   }
6307
6308   if (object_hit)               /* check which object was hit */
6309   {
6310     if ((CAN_PASS_MAGIC_WALL(element) && 
6311          (smashed == EL_MAGIC_WALL ||
6312           smashed == EL_BD_MAGIC_WALL)) ||
6313         (CAN_PASS_DC_MAGIC_WALL(element) &&
6314          smashed == EL_DC_MAGIC_WALL))
6315     {
6316       int xx, yy;
6317       int activated_magic_wall =
6318         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6319          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6320          EL_DC_MAGIC_WALL_ACTIVE);
6321
6322       /* activate magic wall / mill */
6323       SCAN_PLAYFIELD(xx, yy)
6324       {
6325         if (Feld[xx][yy] == smashed)
6326           Feld[xx][yy] = activated_magic_wall;
6327       }
6328
6329       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6330       game.magic_wall_active = TRUE;
6331
6332       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6333                             SND_MAGIC_WALL_ACTIVATING :
6334                             smashed == EL_BD_MAGIC_WALL ?
6335                             SND_BD_MAGIC_WALL_ACTIVATING :
6336                             SND_DC_MAGIC_WALL_ACTIVATING));
6337     }
6338
6339     if (IS_PLAYER(x, y + 1))
6340     {
6341       if (CAN_SMASH_PLAYER(element))
6342       {
6343         KillPlayerUnlessEnemyProtected(x, y + 1);
6344         return;
6345       }
6346     }
6347     else if (smashed == EL_PENGUIN)
6348     {
6349       if (CAN_SMASH_PLAYER(element))
6350       {
6351         Bang(x, y + 1);
6352         return;
6353       }
6354     }
6355     else if (element == EL_BD_DIAMOND)
6356     {
6357       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6358       {
6359         Bang(x, y + 1);
6360         return;
6361       }
6362     }
6363     else if (((element == EL_SP_INFOTRON ||
6364                element == EL_SP_ZONK) &&
6365               (smashed == EL_SP_SNIKSNAK ||
6366                smashed == EL_SP_ELECTRON ||
6367                smashed == EL_SP_DISK_ORANGE)) ||
6368              (element == EL_SP_INFOTRON &&
6369               smashed == EL_SP_DISK_YELLOW))
6370     {
6371       Bang(x, y + 1);
6372       return;
6373     }
6374     else if (CAN_SMASH_EVERYTHING(element))
6375     {
6376       if (IS_CLASSIC_ENEMY(smashed) ||
6377           CAN_EXPLODE_SMASHED(smashed))
6378       {
6379         Bang(x, y + 1);
6380         return;
6381       }
6382       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6383       {
6384         if (smashed == EL_LAMP ||
6385             smashed == EL_LAMP_ACTIVE)
6386         {
6387           Bang(x, y + 1);
6388           return;
6389         }
6390         else if (smashed == EL_NUT)
6391         {
6392           Feld[x][y + 1] = EL_NUT_BREAKING;
6393           PlayLevelSound(x, y, SND_NUT_BREAKING);
6394           RaiseScoreElement(EL_NUT);
6395           return;
6396         }
6397         else if (smashed == EL_PEARL)
6398         {
6399           ResetGfxAnimation(x, y);
6400
6401           Feld[x][y + 1] = EL_PEARL_BREAKING;
6402           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6403           return;
6404         }
6405         else if (smashed == EL_DIAMOND)
6406         {
6407           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6408           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6409           return;
6410         }
6411         else if (IS_BELT_SWITCH(smashed))
6412         {
6413           ToggleBeltSwitch(x, y + 1);
6414         }
6415         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6416                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6417                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6418                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6419         {
6420           ToggleSwitchgateSwitch(x, y + 1);
6421         }
6422         else if (smashed == EL_LIGHT_SWITCH ||
6423                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6424         {
6425           ToggleLightSwitch(x, y + 1);
6426         }
6427         else
6428         {
6429           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6430
6431           CheckElementChangeBySide(x, y + 1, smashed, element,
6432                                    CE_SWITCHED, CH_SIDE_TOP);
6433           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6434                                             CH_SIDE_TOP);
6435         }
6436       }
6437       else
6438       {
6439         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6440       }
6441     }
6442   }
6443
6444   /* play sound of magic wall / mill */
6445   if (!last_line &&
6446       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6447        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6448        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6449   {
6450     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6451       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6452     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6453       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6454     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6455       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6456
6457     return;
6458   }
6459
6460   /* play sound of object that hits the ground */
6461   if (last_line || object_hit)
6462     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6463 }
6464
6465 inline static void TurnRoundExt(int x, int y)
6466 {
6467   static struct
6468   {
6469     int dx, dy;
6470   } move_xy[] =
6471   {
6472     {  0,  0 },
6473     { -1,  0 },
6474     { +1,  0 },
6475     {  0,  0 },
6476     {  0, -1 },
6477     {  0,  0 }, { 0, 0 }, { 0, 0 },
6478     {  0, +1 }
6479   };
6480   static struct
6481   {
6482     int left, right, back;
6483   } turn[] =
6484   {
6485     { 0,        0,              0        },
6486     { MV_DOWN,  MV_UP,          MV_RIGHT },
6487     { MV_UP,    MV_DOWN,        MV_LEFT  },
6488     { 0,        0,              0        },
6489     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6490     { 0,        0,              0        },
6491     { 0,        0,              0        },
6492     { 0,        0,              0        },
6493     { MV_RIGHT, MV_LEFT,        MV_UP    }
6494   };
6495
6496   int element = Feld[x][y];
6497   int move_pattern = element_info[element].move_pattern;
6498
6499   int old_move_dir = MovDir[x][y];
6500   int left_dir  = turn[old_move_dir].left;
6501   int right_dir = turn[old_move_dir].right;
6502   int back_dir  = turn[old_move_dir].back;
6503
6504   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6505   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6506   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6507   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6508
6509   int left_x  = x + left_dx,  left_y  = y + left_dy;
6510   int right_x = x + right_dx, right_y = y + right_dy;
6511   int move_x  = x + move_dx,  move_y  = y + move_dy;
6512
6513   int xx, yy;
6514
6515   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6516   {
6517     TestIfBadThingTouchesOtherBadThing(x, y);
6518
6519     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6520       MovDir[x][y] = right_dir;
6521     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6522       MovDir[x][y] = left_dir;
6523
6524     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6525       MovDelay[x][y] = 9;
6526     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6527       MovDelay[x][y] = 1;
6528   }
6529   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6530   {
6531     TestIfBadThingTouchesOtherBadThing(x, y);
6532
6533     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6534       MovDir[x][y] = left_dir;
6535     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6536       MovDir[x][y] = right_dir;
6537
6538     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6539       MovDelay[x][y] = 9;
6540     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6541       MovDelay[x][y] = 1;
6542   }
6543   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6544   {
6545     TestIfBadThingTouchesOtherBadThing(x, y);
6546
6547     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6548       MovDir[x][y] = left_dir;
6549     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6550       MovDir[x][y] = right_dir;
6551
6552     if (MovDir[x][y] != old_move_dir)
6553       MovDelay[x][y] = 9;
6554   }
6555   else if (element == EL_YAMYAM)
6556   {
6557     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6558     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6559
6560     if (can_turn_left && can_turn_right)
6561       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6562     else if (can_turn_left)
6563       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6564     else if (can_turn_right)
6565       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6566     else
6567       MovDir[x][y] = back_dir;
6568
6569     MovDelay[x][y] = 16 + 16 * RND(3);
6570   }
6571   else if (element == EL_DARK_YAMYAM)
6572   {
6573     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6574                                                          left_x, left_y);
6575     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6576                                                          right_x, right_y);
6577
6578     if (can_turn_left && can_turn_right)
6579       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6580     else if (can_turn_left)
6581       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6582     else if (can_turn_right)
6583       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6584     else
6585       MovDir[x][y] = back_dir;
6586
6587     MovDelay[x][y] = 16 + 16 * RND(3);
6588   }
6589   else if (element == EL_PACMAN)
6590   {
6591     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6592     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6593
6594     if (can_turn_left && can_turn_right)
6595       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6596     else if (can_turn_left)
6597       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6598     else if (can_turn_right)
6599       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6600     else
6601       MovDir[x][y] = back_dir;
6602
6603     MovDelay[x][y] = 6 + RND(40);
6604   }
6605   else if (element == EL_PIG)
6606   {
6607     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6608     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6609     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6610     boolean should_turn_left, should_turn_right, should_move_on;
6611     int rnd_value = 24;
6612     int rnd = RND(rnd_value);
6613
6614     should_turn_left = (can_turn_left &&
6615                         (!can_move_on ||
6616                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6617                                                    y + back_dy + left_dy)));
6618     should_turn_right = (can_turn_right &&
6619                          (!can_move_on ||
6620                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6621                                                     y + back_dy + right_dy)));
6622     should_move_on = (can_move_on &&
6623                       (!can_turn_left ||
6624                        !can_turn_right ||
6625                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6626                                                  y + move_dy + left_dy) ||
6627                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6628                                                  y + move_dy + right_dy)));
6629
6630     if (should_turn_left || should_turn_right || should_move_on)
6631     {
6632       if (should_turn_left && should_turn_right && should_move_on)
6633         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6634                         rnd < 2 * rnd_value / 3 ? right_dir :
6635                         old_move_dir);
6636       else if (should_turn_left && should_turn_right)
6637         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6638       else if (should_turn_left && should_move_on)
6639         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6640       else if (should_turn_right && should_move_on)
6641         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6642       else if (should_turn_left)
6643         MovDir[x][y] = left_dir;
6644       else if (should_turn_right)
6645         MovDir[x][y] = right_dir;
6646       else if (should_move_on)
6647         MovDir[x][y] = old_move_dir;
6648     }
6649     else if (can_move_on && rnd > rnd_value / 8)
6650       MovDir[x][y] = old_move_dir;
6651     else if (can_turn_left && can_turn_right)
6652       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6653     else if (can_turn_left && rnd > rnd_value / 8)
6654       MovDir[x][y] = left_dir;
6655     else if (can_turn_right && rnd > rnd_value/8)
6656       MovDir[x][y] = right_dir;
6657     else
6658       MovDir[x][y] = back_dir;
6659
6660     xx = x + move_xy[MovDir[x][y]].dx;
6661     yy = y + move_xy[MovDir[x][y]].dy;
6662
6663     if (!IN_LEV_FIELD(xx, yy) ||
6664         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6665       MovDir[x][y] = old_move_dir;
6666
6667     MovDelay[x][y] = 0;
6668   }
6669   else if (element == EL_DRAGON)
6670   {
6671     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6672     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6673     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6674     int rnd_value = 24;
6675     int rnd = RND(rnd_value);
6676
6677     if (can_move_on && rnd > rnd_value / 8)
6678       MovDir[x][y] = old_move_dir;
6679     else if (can_turn_left && can_turn_right)
6680       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6681     else if (can_turn_left && rnd > rnd_value / 8)
6682       MovDir[x][y] = left_dir;
6683     else if (can_turn_right && rnd > rnd_value / 8)
6684       MovDir[x][y] = right_dir;
6685     else
6686       MovDir[x][y] = back_dir;
6687
6688     xx = x + move_xy[MovDir[x][y]].dx;
6689     yy = y + move_xy[MovDir[x][y]].dy;
6690
6691     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6692       MovDir[x][y] = old_move_dir;
6693
6694     MovDelay[x][y] = 0;
6695   }
6696   else if (element == EL_MOLE)
6697   {
6698     boolean can_move_on =
6699       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6700                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6701                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6702     if (!can_move_on)
6703     {
6704       boolean can_turn_left =
6705         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6706                               IS_AMOEBOID(Feld[left_x][left_y])));
6707
6708       boolean can_turn_right =
6709         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6710                               IS_AMOEBOID(Feld[right_x][right_y])));
6711
6712       if (can_turn_left && can_turn_right)
6713         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6714       else if (can_turn_left)
6715         MovDir[x][y] = left_dir;
6716       else
6717         MovDir[x][y] = right_dir;
6718     }
6719
6720     if (MovDir[x][y] != old_move_dir)
6721       MovDelay[x][y] = 9;
6722   }
6723   else if (element == EL_BALLOON)
6724   {
6725     MovDir[x][y] = game.wind_direction;
6726     MovDelay[x][y] = 0;
6727   }
6728   else if (element == EL_SPRING)
6729   {
6730     if (MovDir[x][y] & MV_HORIZONTAL)
6731     {
6732       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6733           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6734       {
6735         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6736         ResetGfxAnimation(move_x, move_y);
6737         TEST_DrawLevelField(move_x, move_y);
6738
6739         MovDir[x][y] = back_dir;
6740       }
6741       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6742                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6743         MovDir[x][y] = MV_NONE;
6744     }
6745
6746     MovDelay[x][y] = 0;
6747   }
6748   else if (element == EL_ROBOT ||
6749            element == EL_SATELLITE ||
6750            element == EL_PENGUIN ||
6751            element == EL_EMC_ANDROID)
6752   {
6753     int attr_x = -1, attr_y = -1;
6754
6755     if (AllPlayersGone)
6756     {
6757       attr_x = ExitX;
6758       attr_y = ExitY;
6759     }
6760     else
6761     {
6762       int i;
6763
6764       for (i = 0; i < MAX_PLAYERS; i++)
6765       {
6766         struct PlayerInfo *player = &stored_player[i];
6767         int jx = player->jx, jy = player->jy;
6768
6769         if (!player->active)
6770           continue;
6771
6772         if (attr_x == -1 ||
6773             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6774         {
6775           attr_x = jx;
6776           attr_y = jy;
6777         }
6778       }
6779     }
6780
6781     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6782         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6783          game.engine_version < VERSION_IDENT(3,1,0,0)))
6784     {
6785       attr_x = ZX;
6786       attr_y = ZY;
6787     }
6788
6789     if (element == EL_PENGUIN)
6790     {
6791       int i;
6792       static int xy[4][2] =
6793       {
6794         { 0, -1 },
6795         { -1, 0 },
6796         { +1, 0 },
6797         { 0, +1 }
6798       };
6799
6800       for (i = 0; i < NUM_DIRECTIONS; i++)
6801       {
6802         int ex = x + xy[i][0];
6803         int ey = y + xy[i][1];
6804
6805         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6806                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6807                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6808                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6809         {
6810           attr_x = ex;
6811           attr_y = ey;
6812           break;
6813         }
6814       }
6815     }
6816
6817     MovDir[x][y] = MV_NONE;
6818     if (attr_x < x)
6819       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6820     else if (attr_x > x)
6821       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6822     if (attr_y < y)
6823       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6824     else if (attr_y > y)
6825       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6826
6827     if (element == EL_ROBOT)
6828     {
6829       int newx, newy;
6830
6831       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6832         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6833       Moving2Blocked(x, y, &newx, &newy);
6834
6835       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6836         MovDelay[x][y] = 8 + 8 * !RND(3);
6837       else
6838         MovDelay[x][y] = 16;
6839     }
6840     else if (element == EL_PENGUIN)
6841     {
6842       int newx, newy;
6843
6844       MovDelay[x][y] = 1;
6845
6846       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6847       {
6848         boolean first_horiz = RND(2);
6849         int new_move_dir = MovDir[x][y];
6850
6851         MovDir[x][y] =
6852           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6853         Moving2Blocked(x, y, &newx, &newy);
6854
6855         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6856           return;
6857
6858         MovDir[x][y] =
6859           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6860         Moving2Blocked(x, y, &newx, &newy);
6861
6862         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6863           return;
6864
6865         MovDir[x][y] = old_move_dir;
6866         return;
6867       }
6868     }
6869     else if (element == EL_SATELLITE)
6870     {
6871       int newx, newy;
6872
6873       MovDelay[x][y] = 1;
6874
6875       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6876       {
6877         boolean first_horiz = RND(2);
6878         int new_move_dir = MovDir[x][y];
6879
6880         MovDir[x][y] =
6881           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6882         Moving2Blocked(x, y, &newx, &newy);
6883
6884         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6885           return;
6886
6887         MovDir[x][y] =
6888           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6889         Moving2Blocked(x, y, &newx, &newy);
6890
6891         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6892           return;
6893
6894         MovDir[x][y] = old_move_dir;
6895         return;
6896       }
6897     }
6898     else if (element == EL_EMC_ANDROID)
6899     {
6900       static int check_pos[16] =
6901       {
6902         -1,             /*  0 => (invalid)          */
6903         7,              /*  1 => MV_LEFT            */
6904         3,              /*  2 => MV_RIGHT           */
6905         -1,             /*  3 => (invalid)          */
6906         1,              /*  4 =>            MV_UP   */
6907         0,              /*  5 => MV_LEFT  | MV_UP   */
6908         2,              /*  6 => MV_RIGHT | MV_UP   */
6909         -1,             /*  7 => (invalid)          */
6910         5,              /*  8 =>            MV_DOWN */
6911         6,              /*  9 => MV_LEFT  | MV_DOWN */
6912         4,              /* 10 => MV_RIGHT | MV_DOWN */
6913         -1,             /* 11 => (invalid)          */
6914         -1,             /* 12 => (invalid)          */
6915         -1,             /* 13 => (invalid)          */
6916         -1,             /* 14 => (invalid)          */
6917         -1,             /* 15 => (invalid)          */
6918       };
6919       static struct
6920       {
6921         int dx, dy;
6922         int dir;
6923       } check_xy[8] =
6924       {
6925         { -1, -1,       MV_LEFT  | MV_UP   },
6926         {  0, -1,                  MV_UP   },
6927         { +1, -1,       MV_RIGHT | MV_UP   },
6928         { +1,  0,       MV_RIGHT           },
6929         { +1, +1,       MV_RIGHT | MV_DOWN },
6930         {  0, +1,                  MV_DOWN },
6931         { -1, +1,       MV_LEFT  | MV_DOWN },
6932         { -1,  0,       MV_LEFT            },
6933       };
6934       int start_pos, check_order;
6935       boolean can_clone = FALSE;
6936       int i;
6937
6938       /* check if there is any free field around current position */
6939       for (i = 0; i < 8; i++)
6940       {
6941         int newx = x + check_xy[i].dx;
6942         int newy = y + check_xy[i].dy;
6943
6944         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6945         {
6946           can_clone = TRUE;
6947
6948           break;
6949         }
6950       }
6951
6952       if (can_clone)            /* randomly find an element to clone */
6953       {
6954         can_clone = FALSE;
6955
6956         start_pos = check_pos[RND(8)];
6957         check_order = (RND(2) ? -1 : +1);
6958
6959         for (i = 0; i < 8; i++)
6960         {
6961           int pos_raw = start_pos + i * check_order;
6962           int pos = (pos_raw + 8) % 8;
6963           int newx = x + check_xy[pos].dx;
6964           int newy = y + check_xy[pos].dy;
6965
6966           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6967           {
6968             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6969             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6970
6971             Store[x][y] = Feld[newx][newy];
6972
6973             can_clone = TRUE;
6974
6975             break;
6976           }
6977         }
6978       }
6979
6980       if (can_clone)            /* randomly find a direction to move */
6981       {
6982         can_clone = FALSE;
6983
6984         start_pos = check_pos[RND(8)];
6985         check_order = (RND(2) ? -1 : +1);
6986
6987         for (i = 0; i < 8; i++)
6988         {
6989           int pos_raw = start_pos + i * check_order;
6990           int pos = (pos_raw + 8) % 8;
6991           int newx = x + check_xy[pos].dx;
6992           int newy = y + check_xy[pos].dy;
6993           int new_move_dir = check_xy[pos].dir;
6994
6995           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6996           {
6997             MovDir[x][y] = new_move_dir;
6998             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6999
7000             can_clone = TRUE;
7001
7002             break;
7003           }
7004         }
7005       }
7006
7007       if (can_clone)            /* cloning and moving successful */
7008         return;
7009
7010       /* cannot clone -- try to move towards player */
7011
7012       start_pos = check_pos[MovDir[x][y] & 0x0f];
7013       check_order = (RND(2) ? -1 : +1);
7014
7015       for (i = 0; i < 3; i++)
7016       {
7017         /* first check start_pos, then previous/next or (next/previous) pos */
7018         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7019         int pos = (pos_raw + 8) % 8;
7020         int newx = x + check_xy[pos].dx;
7021         int newy = y + check_xy[pos].dy;
7022         int new_move_dir = check_xy[pos].dir;
7023
7024         if (IS_PLAYER(newx, newy))
7025           break;
7026
7027         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7028         {
7029           MovDir[x][y] = new_move_dir;
7030           MovDelay[x][y] = level.android_move_time * 8 + 1;
7031
7032           break;
7033         }
7034       }
7035     }
7036   }
7037   else if (move_pattern == MV_TURNING_LEFT ||
7038            move_pattern == MV_TURNING_RIGHT ||
7039            move_pattern == MV_TURNING_LEFT_RIGHT ||
7040            move_pattern == MV_TURNING_RIGHT_LEFT ||
7041            move_pattern == MV_TURNING_RANDOM ||
7042            move_pattern == MV_ALL_DIRECTIONS)
7043   {
7044     boolean can_turn_left =
7045       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7046     boolean can_turn_right =
7047       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7048
7049     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7050       return;
7051
7052     if (move_pattern == MV_TURNING_LEFT)
7053       MovDir[x][y] = left_dir;
7054     else if (move_pattern == MV_TURNING_RIGHT)
7055       MovDir[x][y] = right_dir;
7056     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7057       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7058     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7059       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7060     else if (move_pattern == MV_TURNING_RANDOM)
7061       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7062                       can_turn_right && !can_turn_left ? right_dir :
7063                       RND(2) ? left_dir : right_dir);
7064     else if (can_turn_left && can_turn_right)
7065       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7066     else if (can_turn_left)
7067       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7068     else if (can_turn_right)
7069       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7070     else
7071       MovDir[x][y] = back_dir;
7072
7073     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7074   }
7075   else if (move_pattern == MV_HORIZONTAL ||
7076            move_pattern == MV_VERTICAL)
7077   {
7078     if (move_pattern & old_move_dir)
7079       MovDir[x][y] = back_dir;
7080     else if (move_pattern == MV_HORIZONTAL)
7081       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7082     else if (move_pattern == MV_VERTICAL)
7083       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7084
7085     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7086   }
7087   else if (move_pattern & MV_ANY_DIRECTION)
7088   {
7089     MovDir[x][y] = move_pattern;
7090     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7091   }
7092   else if (move_pattern & MV_WIND_DIRECTION)
7093   {
7094     MovDir[x][y] = game.wind_direction;
7095     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7096   }
7097   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7098   {
7099     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7100       MovDir[x][y] = left_dir;
7101     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7102       MovDir[x][y] = right_dir;
7103
7104     if (MovDir[x][y] != old_move_dir)
7105       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7106   }
7107   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7108   {
7109     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7110       MovDir[x][y] = right_dir;
7111     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7112       MovDir[x][y] = left_dir;
7113
7114     if (MovDir[x][y] != old_move_dir)
7115       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7116   }
7117   else if (move_pattern == MV_TOWARDS_PLAYER ||
7118            move_pattern == MV_AWAY_FROM_PLAYER)
7119   {
7120     int attr_x = -1, attr_y = -1;
7121     int newx, newy;
7122     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7123
7124     if (AllPlayersGone)
7125     {
7126       attr_x = ExitX;
7127       attr_y = ExitY;
7128     }
7129     else
7130     {
7131       int i;
7132
7133       for (i = 0; i < MAX_PLAYERS; i++)
7134       {
7135         struct PlayerInfo *player = &stored_player[i];
7136         int jx = player->jx, jy = player->jy;
7137
7138         if (!player->active)
7139           continue;
7140
7141         if (attr_x == -1 ||
7142             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7143         {
7144           attr_x = jx;
7145           attr_y = jy;
7146         }
7147       }
7148     }
7149
7150     MovDir[x][y] = MV_NONE;
7151     if (attr_x < x)
7152       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7153     else if (attr_x > x)
7154       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7155     if (attr_y < y)
7156       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7157     else if (attr_y > y)
7158       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7159
7160     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7161
7162     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7163     {
7164       boolean first_horiz = RND(2);
7165       int new_move_dir = MovDir[x][y];
7166
7167       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7168       {
7169         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7170         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7171
7172         return;
7173       }
7174
7175       MovDir[x][y] =
7176         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7177       Moving2Blocked(x, y, &newx, &newy);
7178
7179       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7180         return;
7181
7182       MovDir[x][y] =
7183         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7184       Moving2Blocked(x, y, &newx, &newy);
7185
7186       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7187         return;
7188
7189       MovDir[x][y] = old_move_dir;
7190     }
7191   }
7192   else if (move_pattern == MV_WHEN_PUSHED ||
7193            move_pattern == MV_WHEN_DROPPED)
7194   {
7195     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7196       MovDir[x][y] = MV_NONE;
7197
7198     MovDelay[x][y] = 0;
7199   }
7200   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7201   {
7202     static int test_xy[7][2] =
7203     {
7204       { 0, -1 },
7205       { -1, 0 },
7206       { +1, 0 },
7207       { 0, +1 },
7208       { 0, -1 },
7209       { -1, 0 },
7210       { +1, 0 },
7211     };
7212     static int test_dir[7] =
7213     {
7214       MV_UP,
7215       MV_LEFT,
7216       MV_RIGHT,
7217       MV_DOWN,
7218       MV_UP,
7219       MV_LEFT,
7220       MV_RIGHT,
7221     };
7222     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7223     int move_preference = -1000000;     /* start with very low preference */
7224     int new_move_dir = MV_NONE;
7225     int start_test = RND(4);
7226     int i;
7227
7228     for (i = 0; i < NUM_DIRECTIONS; i++)
7229     {
7230       int move_dir = test_dir[start_test + i];
7231       int move_dir_preference;
7232
7233       xx = x + test_xy[start_test + i][0];
7234       yy = y + test_xy[start_test + i][1];
7235
7236       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7237           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7238       {
7239         new_move_dir = move_dir;
7240
7241         break;
7242       }
7243
7244       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7245         continue;
7246
7247       move_dir_preference = -1 * RunnerVisit[xx][yy];
7248       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7249         move_dir_preference = PlayerVisit[xx][yy];
7250
7251       if (move_dir_preference > move_preference)
7252       {
7253         /* prefer field that has not been visited for the longest time */
7254         move_preference = move_dir_preference;
7255         new_move_dir = move_dir;
7256       }
7257       else if (move_dir_preference == move_preference &&
7258                move_dir == old_move_dir)
7259       {
7260         /* prefer last direction when all directions are preferred equally */
7261         move_preference = move_dir_preference;
7262         new_move_dir = move_dir;
7263       }
7264     }
7265
7266     MovDir[x][y] = new_move_dir;
7267     if (old_move_dir != new_move_dir)
7268       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7269   }
7270 }
7271
7272 static void TurnRound(int x, int y)
7273 {
7274   int direction = MovDir[x][y];
7275
7276   TurnRoundExt(x, y);
7277
7278   GfxDir[x][y] = MovDir[x][y];
7279
7280   if (direction != MovDir[x][y])
7281     GfxFrame[x][y] = 0;
7282
7283   if (MovDelay[x][y])
7284     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7285
7286   ResetGfxFrame(x, y);
7287 }
7288
7289 static boolean JustBeingPushed(int x, int y)
7290 {
7291   int i;
7292
7293   for (i = 0; i < MAX_PLAYERS; i++)
7294   {
7295     struct PlayerInfo *player = &stored_player[i];
7296
7297     if (player->active && player->is_pushing && player->MovPos)
7298     {
7299       int next_jx = player->jx + (player->jx - player->last_jx);
7300       int next_jy = player->jy + (player->jy - player->last_jy);
7301
7302       if (x == next_jx && y == next_jy)
7303         return TRUE;
7304     }
7305   }
7306
7307   return FALSE;
7308 }
7309
7310 void StartMoving(int x, int y)
7311 {
7312   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7313   int element = Feld[x][y];
7314
7315   if (Stop[x][y])
7316     return;
7317
7318   if (MovDelay[x][y] == 0)
7319     GfxAction[x][y] = ACTION_DEFAULT;
7320
7321   if (CAN_FALL(element) && y < lev_fieldy - 1)
7322   {
7323     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7324         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7325       if (JustBeingPushed(x, y))
7326         return;
7327
7328     if (element == EL_QUICKSAND_FULL)
7329     {
7330       if (IS_FREE(x, y + 1))
7331       {
7332         InitMovingField(x, y, MV_DOWN);
7333         started_moving = TRUE;
7334
7335         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7336 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7337         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7338           Store[x][y] = EL_ROCK;
7339 #else
7340         Store[x][y] = EL_ROCK;
7341 #endif
7342
7343         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7344       }
7345       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7346       {
7347         if (!MovDelay[x][y])
7348         {
7349           MovDelay[x][y] = TILEY + 1;
7350
7351           ResetGfxAnimation(x, y);
7352           ResetGfxAnimation(x, y + 1);
7353         }
7354
7355         if (MovDelay[x][y])
7356         {
7357           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7358           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7359
7360           MovDelay[x][y]--;
7361           if (MovDelay[x][y])
7362             return;
7363         }
7364
7365         Feld[x][y] = EL_QUICKSAND_EMPTY;
7366         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7367         Store[x][y + 1] = Store[x][y];
7368         Store[x][y] = 0;
7369
7370         PlayLevelSoundAction(x, y, ACTION_FILLING);
7371       }
7372       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7373       {
7374         if (!MovDelay[x][y])
7375         {
7376           MovDelay[x][y] = TILEY + 1;
7377
7378           ResetGfxAnimation(x, y);
7379           ResetGfxAnimation(x, y + 1);
7380         }
7381
7382         if (MovDelay[x][y])
7383         {
7384           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7385           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7386
7387           MovDelay[x][y]--;
7388           if (MovDelay[x][y])
7389             return;
7390         }
7391
7392         Feld[x][y] = EL_QUICKSAND_EMPTY;
7393         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7394         Store[x][y + 1] = Store[x][y];
7395         Store[x][y] = 0;
7396
7397         PlayLevelSoundAction(x, y, ACTION_FILLING);
7398       }
7399     }
7400     else if (element == EL_QUICKSAND_FAST_FULL)
7401     {
7402       if (IS_FREE(x, y + 1))
7403       {
7404         InitMovingField(x, y, MV_DOWN);
7405         started_moving = TRUE;
7406
7407         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7408 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7409         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7410           Store[x][y] = EL_ROCK;
7411 #else
7412         Store[x][y] = EL_ROCK;
7413 #endif
7414
7415         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7416       }
7417       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7418       {
7419         if (!MovDelay[x][y])
7420         {
7421           MovDelay[x][y] = TILEY + 1;
7422
7423           ResetGfxAnimation(x, y);
7424           ResetGfxAnimation(x, y + 1);
7425         }
7426
7427         if (MovDelay[x][y])
7428         {
7429           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7430           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7431
7432           MovDelay[x][y]--;
7433           if (MovDelay[x][y])
7434             return;
7435         }
7436
7437         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7438         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7439         Store[x][y + 1] = Store[x][y];
7440         Store[x][y] = 0;
7441
7442         PlayLevelSoundAction(x, y, ACTION_FILLING);
7443       }
7444       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7445       {
7446         if (!MovDelay[x][y])
7447         {
7448           MovDelay[x][y] = TILEY + 1;
7449
7450           ResetGfxAnimation(x, y);
7451           ResetGfxAnimation(x, y + 1);
7452         }
7453
7454         if (MovDelay[x][y])
7455         {
7456           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7457           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7458
7459           MovDelay[x][y]--;
7460           if (MovDelay[x][y])
7461             return;
7462         }
7463
7464         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7465         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7466         Store[x][y + 1] = Store[x][y];
7467         Store[x][y] = 0;
7468
7469         PlayLevelSoundAction(x, y, ACTION_FILLING);
7470       }
7471     }
7472     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7473              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7474     {
7475       InitMovingField(x, y, MV_DOWN);
7476       started_moving = TRUE;
7477
7478       Feld[x][y] = EL_QUICKSAND_FILLING;
7479       Store[x][y] = element;
7480
7481       PlayLevelSoundAction(x, y, ACTION_FILLING);
7482     }
7483     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7484              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7485     {
7486       InitMovingField(x, y, MV_DOWN);
7487       started_moving = TRUE;
7488
7489       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7490       Store[x][y] = element;
7491
7492       PlayLevelSoundAction(x, y, ACTION_FILLING);
7493     }
7494     else if (element == EL_MAGIC_WALL_FULL)
7495     {
7496       if (IS_FREE(x, y + 1))
7497       {
7498         InitMovingField(x, y, MV_DOWN);
7499         started_moving = TRUE;
7500
7501         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7502         Store[x][y] = EL_CHANGED(Store[x][y]);
7503       }
7504       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7505       {
7506         if (!MovDelay[x][y])
7507           MovDelay[x][y] = TILEY / 4 + 1;
7508
7509         if (MovDelay[x][y])
7510         {
7511           MovDelay[x][y]--;
7512           if (MovDelay[x][y])
7513             return;
7514         }
7515
7516         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7517         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7518         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7519         Store[x][y] = 0;
7520       }
7521     }
7522     else if (element == EL_BD_MAGIC_WALL_FULL)
7523     {
7524       if (IS_FREE(x, y + 1))
7525       {
7526         InitMovingField(x, y, MV_DOWN);
7527         started_moving = TRUE;
7528
7529         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7530         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7531       }
7532       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7533       {
7534         if (!MovDelay[x][y])
7535           MovDelay[x][y] = TILEY / 4 + 1;
7536
7537         if (MovDelay[x][y])
7538         {
7539           MovDelay[x][y]--;
7540           if (MovDelay[x][y])
7541             return;
7542         }
7543
7544         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7545         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7546         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7547         Store[x][y] = 0;
7548       }
7549     }
7550     else if (element == EL_DC_MAGIC_WALL_FULL)
7551     {
7552       if (IS_FREE(x, y + 1))
7553       {
7554         InitMovingField(x, y, MV_DOWN);
7555         started_moving = TRUE;
7556
7557         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7558         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7559       }
7560       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7561       {
7562         if (!MovDelay[x][y])
7563           MovDelay[x][y] = TILEY / 4 + 1;
7564
7565         if (MovDelay[x][y])
7566         {
7567           MovDelay[x][y]--;
7568           if (MovDelay[x][y])
7569             return;
7570         }
7571
7572         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7573         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7574         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7575         Store[x][y] = 0;
7576       }
7577     }
7578     else if ((CAN_PASS_MAGIC_WALL(element) &&
7579               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7580                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7581              (CAN_PASS_DC_MAGIC_WALL(element) &&
7582               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7583
7584     {
7585       InitMovingField(x, y, MV_DOWN);
7586       started_moving = TRUE;
7587
7588       Feld[x][y] =
7589         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7590          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7591          EL_DC_MAGIC_WALL_FILLING);
7592       Store[x][y] = element;
7593     }
7594     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7595     {
7596       SplashAcid(x, y + 1);
7597
7598       InitMovingField(x, y, MV_DOWN);
7599       started_moving = TRUE;
7600
7601       Store[x][y] = EL_ACID;
7602     }
7603     else if (
7604              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7605               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7606              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7607               CAN_FALL(element) && WasJustFalling[x][y] &&
7608               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7609
7610              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7611               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7612               (Feld[x][y + 1] == EL_BLOCKED)))
7613     {
7614       /* this is needed for a special case not covered by calling "Impact()"
7615          from "ContinueMoving()": if an element moves to a tile directly below
7616          another element which was just falling on that tile (which was empty
7617          in the previous frame), the falling element above would just stop
7618          instead of smashing the element below (in previous version, the above
7619          element was just checked for "moving" instead of "falling", resulting
7620          in incorrect smashes caused by horizontal movement of the above
7621          element; also, the case of the player being the element to smash was
7622          simply not covered here... :-/ ) */
7623
7624       CheckCollision[x][y] = 0;
7625       CheckImpact[x][y] = 0;
7626
7627       Impact(x, y);
7628     }
7629     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7630     {
7631       if (MovDir[x][y] == MV_NONE)
7632       {
7633         InitMovingField(x, y, MV_DOWN);
7634         started_moving = TRUE;
7635       }
7636     }
7637     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7638     {
7639       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7640         MovDir[x][y] = MV_DOWN;
7641
7642       InitMovingField(x, y, MV_DOWN);
7643       started_moving = TRUE;
7644     }
7645     else if (element == EL_AMOEBA_DROP)
7646     {
7647       Feld[x][y] = EL_AMOEBA_GROWING;
7648       Store[x][y] = EL_AMOEBA_WET;
7649     }
7650     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7651               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7652              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7653              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7654     {
7655       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7656                                 (IS_FREE(x - 1, y + 1) ||
7657                                  Feld[x - 1][y + 1] == EL_ACID));
7658       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7659                                 (IS_FREE(x + 1, y + 1) ||
7660                                  Feld[x + 1][y + 1] == EL_ACID));
7661       boolean can_fall_any  = (can_fall_left || can_fall_right);
7662       boolean can_fall_both = (can_fall_left && can_fall_right);
7663       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7664
7665       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7666       {
7667         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7668           can_fall_right = FALSE;
7669         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7670           can_fall_left = FALSE;
7671         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7672           can_fall_right = FALSE;
7673         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7674           can_fall_left = FALSE;
7675
7676         can_fall_any  = (can_fall_left || can_fall_right);
7677         can_fall_both = FALSE;
7678       }
7679
7680       if (can_fall_both)
7681       {
7682         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7683           can_fall_right = FALSE;       /* slip down on left side */
7684         else
7685           can_fall_left = !(can_fall_right = RND(2));
7686
7687         can_fall_both = FALSE;
7688       }
7689
7690       if (can_fall_any)
7691       {
7692         /* if not determined otherwise, prefer left side for slipping down */
7693         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7694         started_moving = TRUE;
7695       }
7696     }
7697     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7698     {
7699       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7700       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7701       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7702       int belt_dir = game.belt_dir[belt_nr];
7703
7704       if ((belt_dir == MV_LEFT  && left_is_free) ||
7705           (belt_dir == MV_RIGHT && right_is_free))
7706       {
7707         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7708
7709         InitMovingField(x, y, belt_dir);
7710         started_moving = TRUE;
7711
7712         Pushed[x][y] = TRUE;
7713         Pushed[nextx][y] = TRUE;
7714
7715         GfxAction[x][y] = ACTION_DEFAULT;
7716       }
7717       else
7718       {
7719         MovDir[x][y] = 0;       /* if element was moving, stop it */
7720       }
7721     }
7722   }
7723
7724   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7725   if (CAN_MOVE(element) && !started_moving)
7726   {
7727     int move_pattern = element_info[element].move_pattern;
7728     int newx, newy;
7729
7730     Moving2Blocked(x, y, &newx, &newy);
7731
7732     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7733       return;
7734
7735     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7736         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7737     {
7738       WasJustMoving[x][y] = 0;
7739       CheckCollision[x][y] = 0;
7740
7741       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7742
7743       if (Feld[x][y] != element)        /* element has changed */
7744         return;
7745     }
7746
7747     if (!MovDelay[x][y])        /* start new movement phase */
7748     {
7749       /* all objects that can change their move direction after each step
7750          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7751
7752       if (element != EL_YAMYAM &&
7753           element != EL_DARK_YAMYAM &&
7754           element != EL_PACMAN &&
7755           !(move_pattern & MV_ANY_DIRECTION) &&
7756           move_pattern != MV_TURNING_LEFT &&
7757           move_pattern != MV_TURNING_RIGHT &&
7758           move_pattern != MV_TURNING_LEFT_RIGHT &&
7759           move_pattern != MV_TURNING_RIGHT_LEFT &&
7760           move_pattern != MV_TURNING_RANDOM)
7761       {
7762         TurnRound(x, y);
7763
7764         if (MovDelay[x][y] && (element == EL_BUG ||
7765                                element == EL_SPACESHIP ||
7766                                element == EL_SP_SNIKSNAK ||
7767                                element == EL_SP_ELECTRON ||
7768                                element == EL_MOLE))
7769           TEST_DrawLevelField(x, y);
7770       }
7771     }
7772
7773     if (MovDelay[x][y])         /* wait some time before next movement */
7774     {
7775       MovDelay[x][y]--;
7776
7777       if (element == EL_ROBOT ||
7778           element == EL_YAMYAM ||
7779           element == EL_DARK_YAMYAM)
7780       {
7781         DrawLevelElementAnimationIfNeeded(x, y, element);
7782         PlayLevelSoundAction(x, y, ACTION_WAITING);
7783       }
7784       else if (element == EL_SP_ELECTRON)
7785         DrawLevelElementAnimationIfNeeded(x, y, element);
7786       else if (element == EL_DRAGON)
7787       {
7788         int i;
7789         int dir = MovDir[x][y];
7790         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7791         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7792         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7793                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7794                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7795                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7796         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7797
7798         GfxAction[x][y] = ACTION_ATTACKING;
7799
7800         if (IS_PLAYER(x, y))
7801           DrawPlayerField(x, y);
7802         else
7803           TEST_DrawLevelField(x, y);
7804
7805         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7806
7807         for (i = 1; i <= 3; i++)
7808         {
7809           int xx = x + i * dx;
7810           int yy = y + i * dy;
7811           int sx = SCREENX(xx);
7812           int sy = SCREENY(yy);
7813           int flame_graphic = graphic + (i - 1);
7814
7815           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7816             break;
7817
7818           if (MovDelay[x][y])
7819           {
7820             int flamed = MovingOrBlocked2Element(xx, yy);
7821
7822             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7823               Bang(xx, yy);
7824             else
7825               RemoveMovingField(xx, yy);
7826
7827             ChangeDelay[xx][yy] = 0;
7828
7829             Feld[xx][yy] = EL_FLAMES;
7830
7831             if (IN_SCR_FIELD(sx, sy))
7832             {
7833               TEST_DrawLevelFieldCrumbled(xx, yy);
7834               DrawGraphic(sx, sy, flame_graphic, frame);
7835             }
7836           }
7837           else
7838           {
7839             if (Feld[xx][yy] == EL_FLAMES)
7840               Feld[xx][yy] = EL_EMPTY;
7841             TEST_DrawLevelField(xx, yy);
7842           }
7843         }
7844       }
7845
7846       if (MovDelay[x][y])       /* element still has to wait some time */
7847       {
7848         PlayLevelSoundAction(x, y, ACTION_WAITING);
7849
7850         return;
7851       }
7852     }
7853
7854     /* now make next step */
7855
7856     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7857
7858     if (DONT_COLLIDE_WITH(element) &&
7859         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7860         !PLAYER_ENEMY_PROTECTED(newx, newy))
7861     {
7862       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7863
7864       return;
7865     }
7866
7867     else if (CAN_MOVE_INTO_ACID(element) &&
7868              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7869              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7870              (MovDir[x][y] == MV_DOWN ||
7871               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7872     {
7873       SplashAcid(newx, newy);
7874       Store[x][y] = EL_ACID;
7875     }
7876     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7877     {
7878       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7879           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7880           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7881           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7882       {
7883         RemoveField(x, y);
7884         TEST_DrawLevelField(x, y);
7885
7886         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7887         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7888           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7889
7890         local_player->friends_still_needed--;
7891         if (!local_player->friends_still_needed &&
7892             !local_player->GameOver && AllPlayersGone)
7893           PlayerWins(local_player);
7894
7895         return;
7896       }
7897       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7898       {
7899         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7900           TEST_DrawLevelField(newx, newy);
7901         else
7902           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7903       }
7904       else if (!IS_FREE(newx, newy))
7905       {
7906         GfxAction[x][y] = ACTION_WAITING;
7907
7908         if (IS_PLAYER(x, y))
7909           DrawPlayerField(x, y);
7910         else
7911           TEST_DrawLevelField(x, y);
7912
7913         return;
7914       }
7915     }
7916     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7917     {
7918       if (IS_FOOD_PIG(Feld[newx][newy]))
7919       {
7920         if (IS_MOVING(newx, newy))
7921           RemoveMovingField(newx, newy);
7922         else
7923         {
7924           Feld[newx][newy] = EL_EMPTY;
7925           TEST_DrawLevelField(newx, newy);
7926         }
7927
7928         PlayLevelSound(x, y, SND_PIG_DIGGING);
7929       }
7930       else if (!IS_FREE(newx, newy))
7931       {
7932         if (IS_PLAYER(x, y))
7933           DrawPlayerField(x, y);
7934         else
7935           TEST_DrawLevelField(x, y);
7936
7937         return;
7938       }
7939     }
7940     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7941     {
7942       if (Store[x][y] != EL_EMPTY)
7943       {
7944         boolean can_clone = FALSE;
7945         int xx, yy;
7946
7947         /* check if element to clone is still there */
7948         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7949         {
7950           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7951           {
7952             can_clone = TRUE;
7953
7954             break;
7955           }
7956         }
7957
7958         /* cannot clone or target field not free anymore -- do not clone */
7959         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7960           Store[x][y] = EL_EMPTY;
7961       }
7962
7963       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7964       {
7965         if (IS_MV_DIAGONAL(MovDir[x][y]))
7966         {
7967           int diagonal_move_dir = MovDir[x][y];
7968           int stored = Store[x][y];
7969           int change_delay = 8;
7970           int graphic;
7971
7972           /* android is moving diagonally */
7973
7974           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7975
7976           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7977           GfxElement[x][y] = EL_EMC_ANDROID;
7978           GfxAction[x][y] = ACTION_SHRINKING;
7979           GfxDir[x][y] = diagonal_move_dir;
7980           ChangeDelay[x][y] = change_delay;
7981
7982           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7983                                    GfxDir[x][y]);
7984
7985           DrawLevelGraphicAnimation(x, y, graphic);
7986           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7987
7988           if (Feld[newx][newy] == EL_ACID)
7989           {
7990             SplashAcid(newx, newy);
7991
7992             return;
7993           }
7994
7995           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7996
7997           Store[newx][newy] = EL_EMC_ANDROID;
7998           GfxElement[newx][newy] = EL_EMC_ANDROID;
7999           GfxAction[newx][newy] = ACTION_GROWING;
8000           GfxDir[newx][newy] = diagonal_move_dir;
8001           ChangeDelay[newx][newy] = change_delay;
8002
8003           graphic = el_act_dir2img(GfxElement[newx][newy],
8004                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8005
8006           DrawLevelGraphicAnimation(newx, newy, graphic);
8007           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8008
8009           return;
8010         }
8011         else
8012         {
8013           Feld[newx][newy] = EL_EMPTY;
8014           TEST_DrawLevelField(newx, newy);
8015
8016           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8017         }
8018       }
8019       else if (!IS_FREE(newx, newy))
8020       {
8021         return;
8022       }
8023     }
8024     else if (IS_CUSTOM_ELEMENT(element) &&
8025              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8026     {
8027       if (!DigFieldByCE(newx, newy, element))
8028         return;
8029
8030       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8031       {
8032         RunnerVisit[x][y] = FrameCounter;
8033         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8034       }
8035     }
8036     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8037     {
8038       if (!IS_FREE(newx, newy))
8039       {
8040         if (IS_PLAYER(x, y))
8041           DrawPlayerField(x, y);
8042         else
8043           TEST_DrawLevelField(x, y);
8044
8045         return;
8046       }
8047       else
8048       {
8049         boolean wanna_flame = !RND(10);
8050         int dx = newx - x, dy = newy - y;
8051         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8052         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8053         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8054                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8055         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8056                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8057
8058         if ((wanna_flame ||
8059              IS_CLASSIC_ENEMY(element1) ||
8060              IS_CLASSIC_ENEMY(element2)) &&
8061             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8062             element1 != EL_FLAMES && element2 != EL_FLAMES)
8063         {
8064           ResetGfxAnimation(x, y);
8065           GfxAction[x][y] = ACTION_ATTACKING;
8066
8067           if (IS_PLAYER(x, y))
8068             DrawPlayerField(x, y);
8069           else
8070             TEST_DrawLevelField(x, y);
8071
8072           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8073
8074           MovDelay[x][y] = 50;
8075
8076           Feld[newx][newy] = EL_FLAMES;
8077           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8078             Feld[newx1][newy1] = EL_FLAMES;
8079           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8080             Feld[newx2][newy2] = EL_FLAMES;
8081
8082           return;
8083         }
8084       }
8085     }
8086     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8087              Feld[newx][newy] == EL_DIAMOND)
8088     {
8089       if (IS_MOVING(newx, newy))
8090         RemoveMovingField(newx, newy);
8091       else
8092       {
8093         Feld[newx][newy] = EL_EMPTY;
8094         TEST_DrawLevelField(newx, newy);
8095       }
8096
8097       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8098     }
8099     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8100              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8101     {
8102       if (AmoebaNr[newx][newy])
8103       {
8104         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8105         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8106             Feld[newx][newy] == EL_BD_AMOEBA)
8107           AmoebaCnt[AmoebaNr[newx][newy]]--;
8108       }
8109
8110       if (IS_MOVING(newx, newy))
8111       {
8112         RemoveMovingField(newx, newy);
8113       }
8114       else
8115       {
8116         Feld[newx][newy] = EL_EMPTY;
8117         TEST_DrawLevelField(newx, newy);
8118       }
8119
8120       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8121     }
8122     else if ((element == EL_PACMAN || element == EL_MOLE)
8123              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8124     {
8125       if (AmoebaNr[newx][newy])
8126       {
8127         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8128         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8129             Feld[newx][newy] == EL_BD_AMOEBA)
8130           AmoebaCnt[AmoebaNr[newx][newy]]--;
8131       }
8132
8133       if (element == EL_MOLE)
8134       {
8135         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8136         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8137
8138         ResetGfxAnimation(x, y);
8139         GfxAction[x][y] = ACTION_DIGGING;
8140         TEST_DrawLevelField(x, y);
8141
8142         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8143
8144         return;                         /* wait for shrinking amoeba */
8145       }
8146       else      /* element == EL_PACMAN */
8147       {
8148         Feld[newx][newy] = EL_EMPTY;
8149         TEST_DrawLevelField(newx, newy);
8150         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8151       }
8152     }
8153     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8154              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8155               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8156     {
8157       /* wait for shrinking amoeba to completely disappear */
8158       return;
8159     }
8160     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8161     {
8162       /* object was running against a wall */
8163
8164       TurnRound(x, y);
8165
8166       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8167         DrawLevelElementAnimation(x, y, element);
8168
8169       if (DONT_TOUCH(element))
8170         TestIfBadThingTouchesPlayer(x, y);
8171
8172       return;
8173     }
8174
8175     InitMovingField(x, y, MovDir[x][y]);
8176
8177     PlayLevelSoundAction(x, y, ACTION_MOVING);
8178   }
8179
8180   if (MovDir[x][y])
8181     ContinueMoving(x, y);
8182 }
8183
8184 void ContinueMoving(int x, int y)
8185 {
8186   int element = Feld[x][y];
8187   struct ElementInfo *ei = &element_info[element];
8188   int direction = MovDir[x][y];
8189   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8190   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8191   int newx = x + dx, newy = y + dy;
8192   int stored = Store[x][y];
8193   int stored_new = Store[newx][newy];
8194   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8195   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8196   boolean last_line = (newy == lev_fieldy - 1);
8197
8198   MovPos[x][y] += getElementMoveStepsize(x, y);
8199
8200   if (pushed_by_player) /* special case: moving object pushed by player */
8201     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8202
8203   if (ABS(MovPos[x][y]) < TILEX)
8204   {
8205     TEST_DrawLevelField(x, y);
8206
8207     return;     /* element is still moving */
8208   }
8209
8210   /* element reached destination field */
8211
8212   Feld[x][y] = EL_EMPTY;
8213   Feld[newx][newy] = element;
8214   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8215
8216   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8217   {
8218     element = Feld[newx][newy] = EL_ACID;
8219   }
8220   else if (element == EL_MOLE)
8221   {
8222     Feld[x][y] = EL_SAND;
8223
8224     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8225   }
8226   else if (element == EL_QUICKSAND_FILLING)
8227   {
8228     element = Feld[newx][newy] = get_next_element(element);
8229     Store[newx][newy] = Store[x][y];
8230   }
8231   else if (element == EL_QUICKSAND_EMPTYING)
8232   {
8233     Feld[x][y] = get_next_element(element);
8234     element = Feld[newx][newy] = Store[x][y];
8235   }
8236   else if (element == EL_QUICKSAND_FAST_FILLING)
8237   {
8238     element = Feld[newx][newy] = get_next_element(element);
8239     Store[newx][newy] = Store[x][y];
8240   }
8241   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8242   {
8243     Feld[x][y] = get_next_element(element);
8244     element = Feld[newx][newy] = Store[x][y];
8245   }
8246   else if (element == EL_MAGIC_WALL_FILLING)
8247   {
8248     element = Feld[newx][newy] = get_next_element(element);
8249     if (!game.magic_wall_active)
8250       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8251     Store[newx][newy] = Store[x][y];
8252   }
8253   else if (element == EL_MAGIC_WALL_EMPTYING)
8254   {
8255     Feld[x][y] = get_next_element(element);
8256     if (!game.magic_wall_active)
8257       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8258     element = Feld[newx][newy] = Store[x][y];
8259
8260     InitField(newx, newy, FALSE);
8261   }
8262   else if (element == EL_BD_MAGIC_WALL_FILLING)
8263   {
8264     element = Feld[newx][newy] = get_next_element(element);
8265     if (!game.magic_wall_active)
8266       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8267     Store[newx][newy] = Store[x][y];
8268   }
8269   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8270   {
8271     Feld[x][y] = get_next_element(element);
8272     if (!game.magic_wall_active)
8273       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8274     element = Feld[newx][newy] = Store[x][y];
8275
8276     InitField(newx, newy, FALSE);
8277   }
8278   else if (element == EL_DC_MAGIC_WALL_FILLING)
8279   {
8280     element = Feld[newx][newy] = get_next_element(element);
8281     if (!game.magic_wall_active)
8282       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8283     Store[newx][newy] = Store[x][y];
8284   }
8285   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8286   {
8287     Feld[x][y] = get_next_element(element);
8288     if (!game.magic_wall_active)
8289       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8290     element = Feld[newx][newy] = Store[x][y];
8291
8292     InitField(newx, newy, FALSE);
8293   }
8294   else if (element == EL_AMOEBA_DROPPING)
8295   {
8296     Feld[x][y] = get_next_element(element);
8297     element = Feld[newx][newy] = Store[x][y];
8298   }
8299   else if (element == EL_SOKOBAN_OBJECT)
8300   {
8301     if (Back[x][y])
8302       Feld[x][y] = Back[x][y];
8303
8304     if (Back[newx][newy])
8305       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8306
8307     Back[x][y] = Back[newx][newy] = 0;
8308   }
8309
8310   Store[x][y] = EL_EMPTY;
8311   MovPos[x][y] = 0;
8312   MovDir[x][y] = 0;
8313   MovDelay[x][y] = 0;
8314
8315   MovDelay[newx][newy] = 0;
8316
8317   if (CAN_CHANGE_OR_HAS_ACTION(element))
8318   {
8319     /* copy element change control values to new field */
8320     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8321     ChangePage[newx][newy]  = ChangePage[x][y];
8322     ChangeCount[newx][newy] = ChangeCount[x][y];
8323     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8324   }
8325
8326   CustomValue[newx][newy] = CustomValue[x][y];
8327
8328   ChangeDelay[x][y] = 0;
8329   ChangePage[x][y] = -1;
8330   ChangeCount[x][y] = 0;
8331   ChangeEvent[x][y] = -1;
8332
8333   CustomValue[x][y] = 0;
8334
8335   /* copy animation control values to new field */
8336   GfxFrame[newx][newy]  = GfxFrame[x][y];
8337   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8338   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8339   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8340
8341   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8342
8343   /* some elements can leave other elements behind after moving */
8344   if (ei->move_leave_element != EL_EMPTY &&
8345       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8346       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8347   {
8348     int move_leave_element = ei->move_leave_element;
8349
8350     /* this makes it possible to leave the removed element again */
8351     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8352       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8353
8354     Feld[x][y] = move_leave_element;
8355
8356     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8357       MovDir[x][y] = direction;
8358
8359     InitField(x, y, FALSE);
8360
8361     if (GFX_CRUMBLED(Feld[x][y]))
8362       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8363
8364     if (ELEM_IS_PLAYER(move_leave_element))
8365       RelocatePlayer(x, y, move_leave_element);
8366   }
8367
8368   /* do this after checking for left-behind element */
8369   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8370
8371   if (!CAN_MOVE(element) ||
8372       (CAN_FALL(element) && direction == MV_DOWN &&
8373        (element == EL_SPRING ||
8374         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8375         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8376     GfxDir[x][y] = MovDir[newx][newy] = 0;
8377
8378   TEST_DrawLevelField(x, y);
8379   TEST_DrawLevelField(newx, newy);
8380
8381   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8382
8383   /* prevent pushed element from moving on in pushed direction */
8384   if (pushed_by_player && CAN_MOVE(element) &&
8385       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8386       !(element_info[element].move_pattern & direction))
8387     TurnRound(newx, newy);
8388
8389   /* prevent elements on conveyor belt from moving on in last direction */
8390   if (pushed_by_conveyor && CAN_FALL(element) &&
8391       direction & MV_HORIZONTAL)
8392     MovDir[newx][newy] = 0;
8393
8394   if (!pushed_by_player)
8395   {
8396     int nextx = newx + dx, nexty = newy + dy;
8397     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8398
8399     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8400
8401     if (CAN_FALL(element) && direction == MV_DOWN)
8402       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8403
8404     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8405       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8406
8407     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8408       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8409   }
8410
8411   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8412   {
8413     TestIfBadThingTouchesPlayer(newx, newy);
8414     TestIfBadThingTouchesFriend(newx, newy);
8415
8416     if (!IS_CUSTOM_ELEMENT(element))
8417       TestIfBadThingTouchesOtherBadThing(newx, newy);
8418   }
8419   else if (element == EL_PENGUIN)
8420     TestIfFriendTouchesBadThing(newx, newy);
8421
8422   if (DONT_GET_HIT_BY(element))
8423   {
8424     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8425   }
8426
8427   /* give the player one last chance (one more frame) to move away */
8428   if (CAN_FALL(element) && direction == MV_DOWN &&
8429       (last_line || (!IS_FREE(x, newy + 1) &&
8430                      (!IS_PLAYER(x, newy + 1) ||
8431                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8432     Impact(x, newy);
8433
8434   if (pushed_by_player && !game.use_change_when_pushing_bug)
8435   {
8436     int push_side = MV_DIR_OPPOSITE(direction);
8437     struct PlayerInfo *player = PLAYERINFO(x, y);
8438
8439     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8440                                player->index_bit, push_side);
8441     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8442                                         player->index_bit, push_side);
8443   }
8444
8445   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8446     MovDelay[newx][newy] = 1;
8447
8448   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8449
8450   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8451   TestIfElementHitsCustomElement(newx, newy, direction);
8452   TestIfPlayerTouchesCustomElement(newx, newy);
8453   TestIfElementTouchesCustomElement(newx, newy);
8454
8455   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8456       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8457     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8458                              MV_DIR_OPPOSITE(direction));
8459 }
8460
8461 int AmoebeNachbarNr(int ax, int ay)
8462 {
8463   int i;
8464   int element = Feld[ax][ay];
8465   int group_nr = 0;
8466   static int xy[4][2] =
8467   {
8468     { 0, -1 },
8469     { -1, 0 },
8470     { +1, 0 },
8471     { 0, +1 }
8472   };
8473
8474   for (i = 0; i < NUM_DIRECTIONS; i++)
8475   {
8476     int x = ax + xy[i][0];
8477     int y = ay + xy[i][1];
8478
8479     if (!IN_LEV_FIELD(x, y))
8480       continue;
8481
8482     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8483       group_nr = AmoebaNr[x][y];
8484   }
8485
8486   return group_nr;
8487 }
8488
8489 void AmoebenVereinigen(int ax, int ay)
8490 {
8491   int i, x, y, xx, yy;
8492   int new_group_nr = AmoebaNr[ax][ay];
8493   static int xy[4][2] =
8494   {
8495     { 0, -1 },
8496     { -1, 0 },
8497     { +1, 0 },
8498     { 0, +1 }
8499   };
8500
8501   if (new_group_nr == 0)
8502     return;
8503
8504   for (i = 0; i < NUM_DIRECTIONS; i++)
8505   {
8506     x = ax + xy[i][0];
8507     y = ay + xy[i][1];
8508
8509     if (!IN_LEV_FIELD(x, y))
8510       continue;
8511
8512     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8513          Feld[x][y] == EL_BD_AMOEBA ||
8514          Feld[x][y] == EL_AMOEBA_DEAD) &&
8515         AmoebaNr[x][y] != new_group_nr)
8516     {
8517       int old_group_nr = AmoebaNr[x][y];
8518
8519       if (old_group_nr == 0)
8520         return;
8521
8522       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8523       AmoebaCnt[old_group_nr] = 0;
8524       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8525       AmoebaCnt2[old_group_nr] = 0;
8526
8527       SCAN_PLAYFIELD(xx, yy)
8528       {
8529         if (AmoebaNr[xx][yy] == old_group_nr)
8530           AmoebaNr[xx][yy] = new_group_nr;
8531       }
8532     }
8533   }
8534 }
8535
8536 void AmoebeUmwandeln(int ax, int ay)
8537 {
8538   int i, x, y;
8539
8540   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8541   {
8542     int group_nr = AmoebaNr[ax][ay];
8543
8544 #ifdef DEBUG
8545     if (group_nr == 0)
8546     {
8547       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8548       printf("AmoebeUmwandeln(): This should never happen!\n");
8549       return;
8550     }
8551 #endif
8552
8553     SCAN_PLAYFIELD(x, y)
8554     {
8555       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8556       {
8557         AmoebaNr[x][y] = 0;
8558         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8559       }
8560     }
8561
8562     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8563                             SND_AMOEBA_TURNING_TO_GEM :
8564                             SND_AMOEBA_TURNING_TO_ROCK));
8565     Bang(ax, ay);
8566   }
8567   else
8568   {
8569     static int xy[4][2] =
8570     {
8571       { 0, -1 },
8572       { -1, 0 },
8573       { +1, 0 },
8574       { 0, +1 }
8575     };
8576
8577     for (i = 0; i < NUM_DIRECTIONS; i++)
8578     {
8579       x = ax + xy[i][0];
8580       y = ay + xy[i][1];
8581
8582       if (!IN_LEV_FIELD(x, y))
8583         continue;
8584
8585       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8586       {
8587         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8588                               SND_AMOEBA_TURNING_TO_GEM :
8589                               SND_AMOEBA_TURNING_TO_ROCK));
8590         Bang(x, y);
8591       }
8592     }
8593   }
8594 }
8595
8596 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8597 {
8598   int x, y;
8599   int group_nr = AmoebaNr[ax][ay];
8600   boolean done = FALSE;
8601
8602 #ifdef DEBUG
8603   if (group_nr == 0)
8604   {
8605     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8606     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8607     return;
8608   }
8609 #endif
8610
8611   SCAN_PLAYFIELD(x, y)
8612   {
8613     if (AmoebaNr[x][y] == group_nr &&
8614         (Feld[x][y] == EL_AMOEBA_DEAD ||
8615          Feld[x][y] == EL_BD_AMOEBA ||
8616          Feld[x][y] == EL_AMOEBA_GROWING))
8617     {
8618       AmoebaNr[x][y] = 0;
8619       Feld[x][y] = new_element;
8620       InitField(x, y, FALSE);
8621       TEST_DrawLevelField(x, y);
8622       done = TRUE;
8623     }
8624   }
8625
8626   if (done)
8627     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8628                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8629                             SND_BD_AMOEBA_TURNING_TO_GEM));
8630 }
8631
8632 void AmoebeWaechst(int x, int y)
8633 {
8634   static unsigned int sound_delay = 0;
8635   static unsigned int sound_delay_value = 0;
8636
8637   if (!MovDelay[x][y])          /* start new growing cycle */
8638   {
8639     MovDelay[x][y] = 7;
8640
8641     if (DelayReached(&sound_delay, sound_delay_value))
8642     {
8643       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8644       sound_delay_value = 30;
8645     }
8646   }
8647
8648   if (MovDelay[x][y])           /* wait some time before growing bigger */
8649   {
8650     MovDelay[x][y]--;
8651     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8652     {
8653       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8654                                            6 - MovDelay[x][y]);
8655
8656       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8657     }
8658
8659     if (!MovDelay[x][y])
8660     {
8661       Feld[x][y] = Store[x][y];
8662       Store[x][y] = 0;
8663       TEST_DrawLevelField(x, y);
8664     }
8665   }
8666 }
8667
8668 void AmoebaDisappearing(int x, int y)
8669 {
8670   static unsigned int sound_delay = 0;
8671   static unsigned int sound_delay_value = 0;
8672
8673   if (!MovDelay[x][y])          /* start new shrinking cycle */
8674   {
8675     MovDelay[x][y] = 7;
8676
8677     if (DelayReached(&sound_delay, sound_delay_value))
8678       sound_delay_value = 30;
8679   }
8680
8681   if (MovDelay[x][y])           /* wait some time before shrinking */
8682   {
8683     MovDelay[x][y]--;
8684     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8685     {
8686       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8687                                            6 - MovDelay[x][y]);
8688
8689       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8690     }
8691
8692     if (!MovDelay[x][y])
8693     {
8694       Feld[x][y] = EL_EMPTY;
8695       TEST_DrawLevelField(x, y);
8696
8697       /* don't let mole enter this field in this cycle;
8698          (give priority to objects falling to this field from above) */
8699       Stop[x][y] = TRUE;
8700     }
8701   }
8702 }
8703
8704 void AmoebeAbleger(int ax, int ay)
8705 {
8706   int i;
8707   int element = Feld[ax][ay];
8708   int graphic = el2img(element);
8709   int newax = ax, neway = ay;
8710   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8711   static int xy[4][2] =
8712   {
8713     { 0, -1 },
8714     { -1, 0 },
8715     { +1, 0 },
8716     { 0, +1 }
8717   };
8718
8719   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8720   {
8721     Feld[ax][ay] = EL_AMOEBA_DEAD;
8722     TEST_DrawLevelField(ax, ay);
8723     return;
8724   }
8725
8726   if (IS_ANIMATED(graphic))
8727     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8728
8729   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8730     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8731
8732   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8733   {
8734     MovDelay[ax][ay]--;
8735     if (MovDelay[ax][ay])
8736       return;
8737   }
8738
8739   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8740   {
8741     int start = RND(4);
8742     int x = ax + xy[start][0];
8743     int y = ay + xy[start][1];
8744
8745     if (!IN_LEV_FIELD(x, y))
8746       return;
8747
8748     if (IS_FREE(x, y) ||
8749         CAN_GROW_INTO(Feld[x][y]) ||
8750         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8751         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8752     {
8753       newax = x;
8754       neway = y;
8755     }
8756
8757     if (newax == ax && neway == ay)
8758       return;
8759   }
8760   else                          /* normal or "filled" (BD style) amoeba */
8761   {
8762     int start = RND(4);
8763     boolean waiting_for_player = FALSE;
8764
8765     for (i = 0; i < NUM_DIRECTIONS; i++)
8766     {
8767       int j = (start + i) % 4;
8768       int x = ax + xy[j][0];
8769       int y = ay + xy[j][1];
8770
8771       if (!IN_LEV_FIELD(x, y))
8772         continue;
8773
8774       if (IS_FREE(x, y) ||
8775           CAN_GROW_INTO(Feld[x][y]) ||
8776           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8777           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8778       {
8779         newax = x;
8780         neway = y;
8781         break;
8782       }
8783       else if (IS_PLAYER(x, y))
8784         waiting_for_player = TRUE;
8785     }
8786
8787     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8788     {
8789       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8790       {
8791         Feld[ax][ay] = EL_AMOEBA_DEAD;
8792         TEST_DrawLevelField(ax, ay);
8793         AmoebaCnt[AmoebaNr[ax][ay]]--;
8794
8795         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8796         {
8797           if (element == EL_AMOEBA_FULL)
8798             AmoebeUmwandeln(ax, ay);
8799           else if (element == EL_BD_AMOEBA)
8800             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8801         }
8802       }
8803       return;
8804     }
8805     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8806     {
8807       /* amoeba gets larger by growing in some direction */
8808
8809       int new_group_nr = AmoebaNr[ax][ay];
8810
8811 #ifdef DEBUG
8812   if (new_group_nr == 0)
8813   {
8814     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8815     printf("AmoebeAbleger(): This should never happen!\n");
8816     return;
8817   }
8818 #endif
8819
8820       AmoebaNr[newax][neway] = new_group_nr;
8821       AmoebaCnt[new_group_nr]++;
8822       AmoebaCnt2[new_group_nr]++;
8823
8824       /* if amoeba touches other amoeba(s) after growing, unify them */
8825       AmoebenVereinigen(newax, neway);
8826
8827       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8828       {
8829         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8830         return;
8831       }
8832     }
8833   }
8834
8835   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8836       (neway == lev_fieldy - 1 && newax != ax))
8837   {
8838     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8839     Store[newax][neway] = element;
8840   }
8841   else if (neway == ay || element == EL_EMC_DRIPPER)
8842   {
8843     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8844
8845     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8846   }
8847   else
8848   {
8849     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8850     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8851     Store[ax][ay] = EL_AMOEBA_DROP;
8852     ContinueMoving(ax, ay);
8853     return;
8854   }
8855
8856   TEST_DrawLevelField(newax, neway);
8857 }
8858
8859 void Life(int ax, int ay)
8860 {
8861   int x1, y1, x2, y2;
8862   int life_time = 40;
8863   int element = Feld[ax][ay];
8864   int graphic = el2img(element);
8865   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8866                          level.biomaze);
8867   boolean changed = FALSE;
8868
8869   if (IS_ANIMATED(graphic))
8870     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8871
8872   if (Stop[ax][ay])
8873     return;
8874
8875   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8876     MovDelay[ax][ay] = life_time;
8877
8878   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8879   {
8880     MovDelay[ax][ay]--;
8881     if (MovDelay[ax][ay])
8882       return;
8883   }
8884
8885   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8886   {
8887     int xx = ax+x1, yy = ay+y1;
8888     int nachbarn = 0;
8889
8890     if (!IN_LEV_FIELD(xx, yy))
8891       continue;
8892
8893     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8894     {
8895       int x = xx+x2, y = yy+y2;
8896
8897       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8898         continue;
8899
8900       if (((Feld[x][y] == element ||
8901             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8902            !Stop[x][y]) ||
8903           (IS_FREE(x, y) && Stop[x][y]))
8904         nachbarn++;
8905     }
8906
8907     if (xx == ax && yy == ay)           /* field in the middle */
8908     {
8909       if (nachbarn < life_parameter[0] ||
8910           nachbarn > life_parameter[1])
8911       {
8912         Feld[xx][yy] = EL_EMPTY;
8913         if (!Stop[xx][yy])
8914           TEST_DrawLevelField(xx, yy);
8915         Stop[xx][yy] = TRUE;
8916         changed = TRUE;
8917       }
8918     }
8919     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8920     {                                   /* free border field */
8921       if (nachbarn >= life_parameter[2] &&
8922           nachbarn <= life_parameter[3])
8923       {
8924         Feld[xx][yy] = element;
8925         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8926         if (!Stop[xx][yy])
8927           TEST_DrawLevelField(xx, yy);
8928         Stop[xx][yy] = TRUE;
8929         changed = TRUE;
8930       }
8931     }
8932   }
8933
8934   if (changed)
8935     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8936                    SND_GAME_OF_LIFE_GROWING);
8937 }
8938
8939 static void InitRobotWheel(int x, int y)
8940 {
8941   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8942 }
8943
8944 static void RunRobotWheel(int x, int y)
8945 {
8946   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8947 }
8948
8949 static void StopRobotWheel(int x, int y)
8950 {
8951   if (ZX == x && ZY == y)
8952   {
8953     ZX = ZY = -1;
8954
8955     game.robot_wheel_active = FALSE;
8956   }
8957 }
8958
8959 static void InitTimegateWheel(int x, int y)
8960 {
8961   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8962 }
8963
8964 static void RunTimegateWheel(int x, int y)
8965 {
8966   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8967 }
8968
8969 static void InitMagicBallDelay(int x, int y)
8970 {
8971   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8972 }
8973
8974 static void ActivateMagicBall(int bx, int by)
8975 {
8976   int x, y;
8977
8978   if (level.ball_random)
8979   {
8980     int pos_border = RND(8);    /* select one of the eight border elements */
8981     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8982     int xx = pos_content % 3;
8983     int yy = pos_content / 3;
8984
8985     x = bx - 1 + xx;
8986     y = by - 1 + yy;
8987
8988     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8989       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8990   }
8991   else
8992   {
8993     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8994     {
8995       int xx = x - bx + 1;
8996       int yy = y - by + 1;
8997
8998       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8999         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9000     }
9001   }
9002
9003   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9004 }
9005
9006 void CheckExit(int x, int y)
9007 {
9008   if (local_player->gems_still_needed > 0 ||
9009       local_player->sokobanfields_still_needed > 0 ||
9010       local_player->lights_still_needed > 0)
9011   {
9012     int element = Feld[x][y];
9013     int graphic = el2img(element);
9014
9015     if (IS_ANIMATED(graphic))
9016       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9017
9018     return;
9019   }
9020
9021   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9022     return;
9023
9024   Feld[x][y] = EL_EXIT_OPENING;
9025
9026   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9027 }
9028
9029 void CheckExitEM(int x, int y)
9030 {
9031   if (local_player->gems_still_needed > 0 ||
9032       local_player->sokobanfields_still_needed > 0 ||
9033       local_player->lights_still_needed > 0)
9034   {
9035     int element = Feld[x][y];
9036     int graphic = el2img(element);
9037
9038     if (IS_ANIMATED(graphic))
9039       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9040
9041     return;
9042   }
9043
9044   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9045     return;
9046
9047   Feld[x][y] = EL_EM_EXIT_OPENING;
9048
9049   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9050 }
9051
9052 void CheckExitSteel(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_STEEL_EXIT_OPENING;
9071
9072   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9073 }
9074
9075 void CheckExitSteelEM(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_STEEL_EXIT_OPENING;
9094
9095   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9096 }
9097
9098 void CheckExitSP(int x, int y)
9099 {
9100   if (local_player->gems_still_needed > 0)
9101   {
9102     int element = Feld[x][y];
9103     int graphic = el2img(element);
9104
9105     if (IS_ANIMATED(graphic))
9106       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9107
9108     return;
9109   }
9110
9111   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9112     return;
9113
9114   Feld[x][y] = EL_SP_EXIT_OPENING;
9115
9116   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9117 }
9118
9119 static void CloseAllOpenTimegates()
9120 {
9121   int x, y;
9122
9123   SCAN_PLAYFIELD(x, y)
9124   {
9125     int element = Feld[x][y];
9126
9127     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9128     {
9129       Feld[x][y] = EL_TIMEGATE_CLOSING;
9130
9131       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9132     }
9133   }
9134 }
9135
9136 void DrawTwinkleOnField(int x, int y)
9137 {
9138   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9139     return;
9140
9141   if (Feld[x][y] == EL_BD_DIAMOND)
9142     return;
9143
9144   if (MovDelay[x][y] == 0)      /* next animation frame */
9145     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9146
9147   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9148   {
9149     MovDelay[x][y]--;
9150
9151     DrawLevelElementAnimation(x, y, Feld[x][y]);
9152
9153     if (MovDelay[x][y] != 0)
9154     {
9155       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9156                                            10 - MovDelay[x][y]);
9157
9158       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9159     }
9160   }
9161 }
9162
9163 void MauerWaechst(int x, int y)
9164 {
9165   int delay = 6;
9166
9167   if (!MovDelay[x][y])          /* next animation frame */
9168     MovDelay[x][y] = 3 * delay;
9169
9170   if (MovDelay[x][y])           /* wait some time before next frame */
9171   {
9172     MovDelay[x][y]--;
9173
9174     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9175     {
9176       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9177       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9178
9179       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9180     }
9181
9182     if (!MovDelay[x][y])
9183     {
9184       if (MovDir[x][y] == MV_LEFT)
9185       {
9186         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9187           TEST_DrawLevelField(x - 1, y);
9188       }
9189       else if (MovDir[x][y] == MV_RIGHT)
9190       {
9191         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9192           TEST_DrawLevelField(x + 1, y);
9193       }
9194       else if (MovDir[x][y] == MV_UP)
9195       {
9196         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9197           TEST_DrawLevelField(x, y - 1);
9198       }
9199       else
9200       {
9201         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9202           TEST_DrawLevelField(x, y + 1);
9203       }
9204
9205       Feld[x][y] = Store[x][y];
9206       Store[x][y] = 0;
9207       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9208       TEST_DrawLevelField(x, y);
9209     }
9210   }
9211 }
9212
9213 void MauerAbleger(int ax, int ay)
9214 {
9215   int element = Feld[ax][ay];
9216   int graphic = el2img(element);
9217   boolean oben_frei = FALSE, unten_frei = FALSE;
9218   boolean links_frei = FALSE, rechts_frei = FALSE;
9219   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9220   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9221   boolean new_wall = FALSE;
9222
9223   if (IS_ANIMATED(graphic))
9224     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9225
9226   if (!MovDelay[ax][ay])        /* start building new wall */
9227     MovDelay[ax][ay] = 6;
9228
9229   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9230   {
9231     MovDelay[ax][ay]--;
9232     if (MovDelay[ax][ay])
9233       return;
9234   }
9235
9236   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9237     oben_frei = TRUE;
9238   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9239     unten_frei = TRUE;
9240   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9241     links_frei = TRUE;
9242   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9243     rechts_frei = TRUE;
9244
9245   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9246       element == EL_EXPANDABLE_WALL_ANY)
9247   {
9248     if (oben_frei)
9249     {
9250       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9251       Store[ax][ay-1] = element;
9252       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9253       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9254         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9255                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9256       new_wall = TRUE;
9257     }
9258     if (unten_frei)
9259     {
9260       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9261       Store[ax][ay+1] = element;
9262       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9263       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9264         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9265                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9266       new_wall = TRUE;
9267     }
9268   }
9269
9270   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9271       element == EL_EXPANDABLE_WALL_ANY ||
9272       element == EL_EXPANDABLE_WALL ||
9273       element == EL_BD_EXPANDABLE_WALL)
9274   {
9275     if (links_frei)
9276     {
9277       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9278       Store[ax-1][ay] = element;
9279       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9280       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9281         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9282                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9283       new_wall = TRUE;
9284     }
9285
9286     if (rechts_frei)
9287     {
9288       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9289       Store[ax+1][ay] = element;
9290       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9291       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9292         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9293                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9294       new_wall = TRUE;
9295     }
9296   }
9297
9298   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9299     TEST_DrawLevelField(ax, ay);
9300
9301   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9302     oben_massiv = TRUE;
9303   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9304     unten_massiv = TRUE;
9305   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9306     links_massiv = TRUE;
9307   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9308     rechts_massiv = TRUE;
9309
9310   if (((oben_massiv && unten_massiv) ||
9311        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9312        element == EL_EXPANDABLE_WALL) &&
9313       ((links_massiv && rechts_massiv) ||
9314        element == EL_EXPANDABLE_WALL_VERTICAL))
9315     Feld[ax][ay] = EL_WALL;
9316
9317   if (new_wall)
9318     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9319 }
9320
9321 void MauerAblegerStahl(int ax, int ay)
9322 {
9323   int element = Feld[ax][ay];
9324   int graphic = el2img(element);
9325   boolean oben_frei = FALSE, unten_frei = FALSE;
9326   boolean links_frei = FALSE, rechts_frei = FALSE;
9327   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9328   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9329   boolean new_wall = FALSE;
9330
9331   if (IS_ANIMATED(graphic))
9332     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9333
9334   if (!MovDelay[ax][ay])        /* start building new wall */
9335     MovDelay[ax][ay] = 6;
9336
9337   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9338   {
9339     MovDelay[ax][ay]--;
9340     if (MovDelay[ax][ay])
9341       return;
9342   }
9343
9344   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9345     oben_frei = TRUE;
9346   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9347     unten_frei = TRUE;
9348   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9349     links_frei = TRUE;
9350   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9351     rechts_frei = TRUE;
9352
9353   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9354       element == EL_EXPANDABLE_STEELWALL_ANY)
9355   {
9356     if (oben_frei)
9357     {
9358       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9359       Store[ax][ay-1] = element;
9360       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9361       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9362         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9363                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9364       new_wall = TRUE;
9365     }
9366     if (unten_frei)
9367     {
9368       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9369       Store[ax][ay+1] = element;
9370       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9371       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9372         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9373                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9374       new_wall = TRUE;
9375     }
9376   }
9377
9378   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9379       element == EL_EXPANDABLE_STEELWALL_ANY)
9380   {
9381     if (links_frei)
9382     {
9383       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9384       Store[ax-1][ay] = element;
9385       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9386       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9387         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9388                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9389       new_wall = TRUE;
9390     }
9391
9392     if (rechts_frei)
9393     {
9394       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9395       Store[ax+1][ay] = element;
9396       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9397       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9398         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9399                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9400       new_wall = TRUE;
9401     }
9402   }
9403
9404   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9405     oben_massiv = TRUE;
9406   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9407     unten_massiv = TRUE;
9408   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9409     links_massiv = TRUE;
9410   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9411     rechts_massiv = TRUE;
9412
9413   if (((oben_massiv && unten_massiv) ||
9414        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9415       ((links_massiv && rechts_massiv) ||
9416        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9417     Feld[ax][ay] = EL_STEELWALL;
9418
9419   if (new_wall)
9420     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9421 }
9422
9423 void CheckForDragon(int x, int y)
9424 {
9425   int i, j;
9426   boolean dragon_found = FALSE;
9427   static int xy[4][2] =
9428   {
9429     { 0, -1 },
9430     { -1, 0 },
9431     { +1, 0 },
9432     { 0, +1 }
9433   };
9434
9435   for (i = 0; i < NUM_DIRECTIONS; i++)
9436   {
9437     for (j = 0; j < 4; j++)
9438     {
9439       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9440
9441       if (IN_LEV_FIELD(xx, yy) &&
9442           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9443       {
9444         if (Feld[xx][yy] == EL_DRAGON)
9445           dragon_found = TRUE;
9446       }
9447       else
9448         break;
9449     }
9450   }
9451
9452   if (!dragon_found)
9453   {
9454     for (i = 0; i < NUM_DIRECTIONS; i++)
9455     {
9456       for (j = 0; j < 3; j++)
9457       {
9458         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9459   
9460         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9461         {
9462           Feld[xx][yy] = EL_EMPTY;
9463           TEST_DrawLevelField(xx, yy);
9464         }
9465         else
9466           break;
9467       }
9468     }
9469   }
9470 }
9471
9472 static void InitBuggyBase(int x, int y)
9473 {
9474   int element = Feld[x][y];
9475   int activating_delay = FRAMES_PER_SECOND / 4;
9476
9477   ChangeDelay[x][y] =
9478     (element == EL_SP_BUGGY_BASE ?
9479      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9480      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9481      activating_delay :
9482      element == EL_SP_BUGGY_BASE_ACTIVE ?
9483      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9484 }
9485
9486 static void WarnBuggyBase(int x, int y)
9487 {
9488   int i;
9489   static int xy[4][2] =
9490   {
9491     { 0, -1 },
9492     { -1, 0 },
9493     { +1, 0 },
9494     { 0, +1 }
9495   };
9496
9497   for (i = 0; i < NUM_DIRECTIONS; i++)
9498   {
9499     int xx = x + xy[i][0];
9500     int yy = y + xy[i][1];
9501
9502     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9503     {
9504       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9505
9506       break;
9507     }
9508   }
9509 }
9510
9511 static void InitTrap(int x, int y)
9512 {
9513   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9514 }
9515
9516 static void ActivateTrap(int x, int y)
9517 {
9518   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9519 }
9520
9521 static void ChangeActiveTrap(int x, int y)
9522 {
9523   int graphic = IMG_TRAP_ACTIVE;
9524
9525   /* if new animation frame was drawn, correct crumbled sand border */
9526   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9527     TEST_DrawLevelFieldCrumbled(x, y);
9528 }
9529
9530 static int getSpecialActionElement(int element, int number, int base_element)
9531 {
9532   return (element != EL_EMPTY ? element :
9533           number != -1 ? base_element + number - 1 :
9534           EL_EMPTY);
9535 }
9536
9537 static int getModifiedActionNumber(int value_old, int operator, int operand,
9538                                    int value_min, int value_max)
9539 {
9540   int value_new = (operator == CA_MODE_SET      ? operand :
9541                    operator == CA_MODE_ADD      ? value_old + operand :
9542                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9543                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9544                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9545                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9546                    value_old);
9547
9548   return (value_new < value_min ? value_min :
9549           value_new > value_max ? value_max :
9550           value_new);
9551 }
9552
9553 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9554 {
9555   struct ElementInfo *ei = &element_info[element];
9556   struct ElementChangeInfo *change = &ei->change_page[page];
9557   int target_element = change->target_element;
9558   int action_type = change->action_type;
9559   int action_mode = change->action_mode;
9560   int action_arg = change->action_arg;
9561   int action_element = change->action_element;
9562   int i;
9563
9564   if (!change->has_action)
9565     return;
9566
9567   /* ---------- determine action paramater values -------------------------- */
9568
9569   int level_time_value =
9570     (level.time > 0 ? TimeLeft :
9571      TimePlayed);
9572
9573   int action_arg_element_raw =
9574     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9575      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9576      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9577      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9578      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9579      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9580      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9581      EL_EMPTY);
9582   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9583
9584   int action_arg_direction =
9585     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9586      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9587      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9588      change->actual_trigger_side :
9589      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9590      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9591      MV_NONE);
9592
9593   int action_arg_number_min =
9594     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9595      CA_ARG_MIN);
9596
9597   int action_arg_number_max =
9598     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9599      action_type == CA_SET_LEVEL_GEMS ? 999 :
9600      action_type == CA_SET_LEVEL_TIME ? 9999 :
9601      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9602      action_type == CA_SET_CE_VALUE ? 9999 :
9603      action_type == CA_SET_CE_SCORE ? 9999 :
9604      CA_ARG_MAX);
9605
9606   int action_arg_number_reset =
9607     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9608      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9609      action_type == CA_SET_LEVEL_TIME ? level.time :
9610      action_type == CA_SET_LEVEL_SCORE ? 0 :
9611      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9612      action_type == CA_SET_CE_SCORE ? 0 :
9613      0);
9614
9615   int action_arg_number =
9616     (action_arg <= CA_ARG_MAX ? action_arg :
9617      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9618      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9619      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9620      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9621      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9622      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9623      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9624      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9625      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9626      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9627      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9628      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9629      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9630      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9631      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9632      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9633      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9634      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9635      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9636      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9637      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9638      -1);
9639
9640   int action_arg_number_old =
9641     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9642      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9643      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9644      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9645      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9646      0);
9647
9648   int action_arg_number_new =
9649     getModifiedActionNumber(action_arg_number_old,
9650                             action_mode, action_arg_number,
9651                             action_arg_number_min, action_arg_number_max);
9652
9653   int trigger_player_bits =
9654     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9655      change->actual_trigger_player_bits : change->trigger_player);
9656
9657   int action_arg_player_bits =
9658     (action_arg >= CA_ARG_PLAYER_1 &&
9659      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9660      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9661      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9662      PLAYER_BITS_ANY);
9663
9664   /* ---------- execute action  -------------------------------------------- */
9665
9666   switch (action_type)
9667   {
9668     case CA_NO_ACTION:
9669     {
9670       return;
9671     }
9672
9673     /* ---------- level actions  ------------------------------------------- */
9674
9675     case CA_RESTART_LEVEL:
9676     {
9677       game.restart_level = TRUE;
9678
9679       break;
9680     }
9681
9682     case CA_SHOW_ENVELOPE:
9683     {
9684       int element = getSpecialActionElement(action_arg_element,
9685                                             action_arg_number, EL_ENVELOPE_1);
9686
9687       if (IS_ENVELOPE(element))
9688         local_player->show_envelope = element;
9689
9690       break;
9691     }
9692
9693     case CA_SET_LEVEL_TIME:
9694     {
9695       if (level.time > 0)       /* only modify limited time value */
9696       {
9697         TimeLeft = action_arg_number_new;
9698
9699         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9700
9701         DisplayGameControlValues();
9702
9703         if (!TimeLeft && setup.time_limit)
9704           for (i = 0; i < MAX_PLAYERS; i++)
9705             KillPlayer(&stored_player[i]);
9706       }
9707
9708       break;
9709     }
9710
9711     case CA_SET_LEVEL_SCORE:
9712     {
9713       local_player->score = action_arg_number_new;
9714
9715       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9716
9717       DisplayGameControlValues();
9718
9719       break;
9720     }
9721
9722     case CA_SET_LEVEL_GEMS:
9723     {
9724       local_player->gems_still_needed = action_arg_number_new;
9725
9726       game.snapshot.collected_item = TRUE;
9727
9728       game_panel_controls[GAME_PANEL_GEMS].value =
9729         local_player->gems_still_needed;
9730
9731       DisplayGameControlValues();
9732
9733       break;
9734     }
9735
9736     case CA_SET_LEVEL_WIND:
9737     {
9738       game.wind_direction = action_arg_direction;
9739
9740       break;
9741     }
9742
9743     case CA_SET_LEVEL_RANDOM_SEED:
9744     {
9745       /* ensure that setting a new random seed while playing is predictable */
9746       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9747
9748       break;
9749     }
9750
9751     /* ---------- player actions  ------------------------------------------ */
9752
9753     case CA_MOVE_PLAYER:
9754     {
9755       /* automatically move to the next field in specified direction */
9756       for (i = 0; i < MAX_PLAYERS; i++)
9757         if (trigger_player_bits & (1 << i))
9758           stored_player[i].programmed_action = action_arg_direction;
9759
9760       break;
9761     }
9762
9763     case CA_EXIT_PLAYER:
9764     {
9765       for (i = 0; i < MAX_PLAYERS; i++)
9766         if (action_arg_player_bits & (1 << i))
9767           PlayerWins(&stored_player[i]);
9768
9769       break;
9770     }
9771
9772     case CA_KILL_PLAYER:
9773     {
9774       for (i = 0; i < MAX_PLAYERS; i++)
9775         if (action_arg_player_bits & (1 << i))
9776           KillPlayer(&stored_player[i]);
9777
9778       break;
9779     }
9780
9781     case CA_SET_PLAYER_KEYS:
9782     {
9783       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9784       int element = getSpecialActionElement(action_arg_element,
9785                                             action_arg_number, EL_KEY_1);
9786
9787       if (IS_KEY(element))
9788       {
9789         for (i = 0; i < MAX_PLAYERS; i++)
9790         {
9791           if (trigger_player_bits & (1 << i))
9792           {
9793             stored_player[i].key[KEY_NR(element)] = key_state;
9794
9795             DrawGameDoorValues();
9796           }
9797         }
9798       }
9799
9800       break;
9801     }
9802
9803     case CA_SET_PLAYER_SPEED:
9804     {
9805       for (i = 0; i < MAX_PLAYERS; i++)
9806       {
9807         if (trigger_player_bits & (1 << i))
9808         {
9809           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9810
9811           if (action_arg == CA_ARG_SPEED_FASTER &&
9812               stored_player[i].cannot_move)
9813           {
9814             action_arg_number = STEPSIZE_VERY_SLOW;
9815           }
9816           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9817                    action_arg == CA_ARG_SPEED_FASTER)
9818           {
9819             action_arg_number = 2;
9820             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9821                            CA_MODE_MULTIPLY);
9822           }
9823           else if (action_arg == CA_ARG_NUMBER_RESET)
9824           {
9825             action_arg_number = level.initial_player_stepsize[i];
9826           }
9827
9828           move_stepsize =
9829             getModifiedActionNumber(move_stepsize,
9830                                     action_mode,
9831                                     action_arg_number,
9832                                     action_arg_number_min,
9833                                     action_arg_number_max);
9834
9835           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9836         }
9837       }
9838
9839       break;
9840     }
9841
9842     case CA_SET_PLAYER_SHIELD:
9843     {
9844       for (i = 0; i < MAX_PLAYERS; i++)
9845       {
9846         if (trigger_player_bits & (1 << i))
9847         {
9848           if (action_arg == CA_ARG_SHIELD_OFF)
9849           {
9850             stored_player[i].shield_normal_time_left = 0;
9851             stored_player[i].shield_deadly_time_left = 0;
9852           }
9853           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9854           {
9855             stored_player[i].shield_normal_time_left = 999999;
9856           }
9857           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9858           {
9859             stored_player[i].shield_normal_time_left = 999999;
9860             stored_player[i].shield_deadly_time_left = 999999;
9861           }
9862         }
9863       }
9864
9865       break;
9866     }
9867
9868     case CA_SET_PLAYER_GRAVITY:
9869     {
9870       for (i = 0; i < MAX_PLAYERS; i++)
9871       {
9872         if (trigger_player_bits & (1 << i))
9873         {
9874           stored_player[i].gravity =
9875             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9876              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9877              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9878              stored_player[i].gravity);
9879         }
9880       }
9881
9882       break;
9883     }
9884
9885     case CA_SET_PLAYER_ARTWORK:
9886     {
9887       for (i = 0; i < MAX_PLAYERS; i++)
9888       {
9889         if (trigger_player_bits & (1 << i))
9890         {
9891           int artwork_element = action_arg_element;
9892
9893           if (action_arg == CA_ARG_ELEMENT_RESET)
9894             artwork_element =
9895               (level.use_artwork_element[i] ? level.artwork_element[i] :
9896                stored_player[i].element_nr);
9897
9898           if (stored_player[i].artwork_element != artwork_element)
9899             stored_player[i].Frame = 0;
9900
9901           stored_player[i].artwork_element = artwork_element;
9902
9903           SetPlayerWaiting(&stored_player[i], FALSE);
9904
9905           /* set number of special actions for bored and sleeping animation */
9906           stored_player[i].num_special_action_bored =
9907             get_num_special_action(artwork_element,
9908                                    ACTION_BORING_1, ACTION_BORING_LAST);
9909           stored_player[i].num_special_action_sleeping =
9910             get_num_special_action(artwork_element,
9911                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9912         }
9913       }
9914
9915       break;
9916     }
9917
9918     case CA_SET_PLAYER_INVENTORY:
9919     {
9920       for (i = 0; i < MAX_PLAYERS; i++)
9921       {
9922         struct PlayerInfo *player = &stored_player[i];
9923         int j, k;
9924
9925         if (trigger_player_bits & (1 << i))
9926         {
9927           int inventory_element = action_arg_element;
9928
9929           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9930               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9931               action_arg == CA_ARG_ELEMENT_ACTION)
9932           {
9933             int element = inventory_element;
9934             int collect_count = element_info[element].collect_count_initial;
9935
9936             if (!IS_CUSTOM_ELEMENT(element))
9937               collect_count = 1;
9938
9939             if (collect_count == 0)
9940               player->inventory_infinite_element = element;
9941             else
9942               for (k = 0; k < collect_count; k++)
9943                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9944                   player->inventory_element[player->inventory_size++] =
9945                     element;
9946           }
9947           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9948                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9949                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9950           {
9951             if (player->inventory_infinite_element != EL_UNDEFINED &&
9952                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9953                                      action_arg_element_raw))
9954               player->inventory_infinite_element = EL_UNDEFINED;
9955
9956             for (k = 0, j = 0; j < player->inventory_size; j++)
9957             {
9958               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9959                                         action_arg_element_raw))
9960                 player->inventory_element[k++] = player->inventory_element[j];
9961             }
9962
9963             player->inventory_size = k;
9964           }
9965           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9966           {
9967             if (player->inventory_size > 0)
9968             {
9969               for (j = 0; j < player->inventory_size - 1; j++)
9970                 player->inventory_element[j] = player->inventory_element[j + 1];
9971
9972               player->inventory_size--;
9973             }
9974           }
9975           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9976           {
9977             if (player->inventory_size > 0)
9978               player->inventory_size--;
9979           }
9980           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9981           {
9982             player->inventory_infinite_element = EL_UNDEFINED;
9983             player->inventory_size = 0;
9984           }
9985           else if (action_arg == CA_ARG_INVENTORY_RESET)
9986           {
9987             player->inventory_infinite_element = EL_UNDEFINED;
9988             player->inventory_size = 0;
9989
9990             if (level.use_initial_inventory[i])
9991             {
9992               for (j = 0; j < level.initial_inventory_size[i]; j++)
9993               {
9994                 int element = level.initial_inventory_content[i][j];
9995                 int collect_count = element_info[element].collect_count_initial;
9996
9997                 if (!IS_CUSTOM_ELEMENT(element))
9998                   collect_count = 1;
9999
10000                 if (collect_count == 0)
10001                   player->inventory_infinite_element = element;
10002                 else
10003                   for (k = 0; k < collect_count; k++)
10004                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10005                       player->inventory_element[player->inventory_size++] =
10006                         element;
10007               }
10008             }
10009           }
10010         }
10011       }
10012
10013       break;
10014     }
10015
10016     /* ---------- CE actions  ---------------------------------------------- */
10017
10018     case CA_SET_CE_VALUE:
10019     {
10020       int last_ce_value = CustomValue[x][y];
10021
10022       CustomValue[x][y] = action_arg_number_new;
10023
10024       if (CustomValue[x][y] != last_ce_value)
10025       {
10026         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10027         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10028
10029         if (CustomValue[x][y] == 0)
10030         {
10031           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10032           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10033         }
10034       }
10035
10036       break;
10037     }
10038
10039     case CA_SET_CE_SCORE:
10040     {
10041       int last_ce_score = ei->collect_score;
10042
10043       ei->collect_score = action_arg_number_new;
10044
10045       if (ei->collect_score != last_ce_score)
10046       {
10047         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10048         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10049
10050         if (ei->collect_score == 0)
10051         {
10052           int xx, yy;
10053
10054           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10055           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10056
10057           /*
10058             This is a very special case that seems to be a mixture between
10059             CheckElementChange() and CheckTriggeredElementChange(): while
10060             the first one only affects single elements that are triggered
10061             directly, the second one affects multiple elements in the playfield
10062             that are triggered indirectly by another element. This is a third
10063             case: Changing the CE score always affects multiple identical CEs,
10064             so every affected CE must be checked, not only the single CE for
10065             which the CE score was changed in the first place (as every instance
10066             of that CE shares the same CE score, and therefore also can change)!
10067           */
10068           SCAN_PLAYFIELD(xx, yy)
10069           {
10070             if (Feld[xx][yy] == element)
10071               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10072                                  CE_SCORE_GETS_ZERO);
10073           }
10074         }
10075       }
10076
10077       break;
10078     }
10079
10080     case CA_SET_CE_ARTWORK:
10081     {
10082       int artwork_element = action_arg_element;
10083       boolean reset_frame = FALSE;
10084       int xx, yy;
10085
10086       if (action_arg == CA_ARG_ELEMENT_RESET)
10087         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10088                            element);
10089
10090       if (ei->gfx_element != artwork_element)
10091         reset_frame = TRUE;
10092
10093       ei->gfx_element = artwork_element;
10094
10095       SCAN_PLAYFIELD(xx, yy)
10096       {
10097         if (Feld[xx][yy] == element)
10098         {
10099           if (reset_frame)
10100           {
10101             ResetGfxAnimation(xx, yy);
10102             ResetRandomAnimationValue(xx, yy);
10103           }
10104
10105           TEST_DrawLevelField(xx, yy);
10106         }
10107       }
10108
10109       break;
10110     }
10111
10112     /* ---------- engine actions  ------------------------------------------ */
10113
10114     case CA_SET_ENGINE_SCAN_MODE:
10115     {
10116       InitPlayfieldScanMode(action_arg);
10117
10118       break;
10119     }
10120
10121     default:
10122       break;
10123   }
10124 }
10125
10126 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10127 {
10128   int old_element = Feld[x][y];
10129   int new_element = GetElementFromGroupElement(element);
10130   int previous_move_direction = MovDir[x][y];
10131   int last_ce_value = CustomValue[x][y];
10132   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10133   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10134   boolean add_player_onto_element = (new_element_is_player &&
10135                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10136                                      IS_WALKABLE(old_element));
10137
10138   if (!add_player_onto_element)
10139   {
10140     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10141       RemoveMovingField(x, y);
10142     else
10143       RemoveField(x, y);
10144
10145     Feld[x][y] = new_element;
10146
10147     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10148       MovDir[x][y] = previous_move_direction;
10149
10150     if (element_info[new_element].use_last_ce_value)
10151       CustomValue[x][y] = last_ce_value;
10152
10153     InitField_WithBug1(x, y, FALSE);
10154
10155     new_element = Feld[x][y];   /* element may have changed */
10156
10157     ResetGfxAnimation(x, y);
10158     ResetRandomAnimationValue(x, y);
10159
10160     TEST_DrawLevelField(x, y);
10161
10162     if (GFX_CRUMBLED(new_element))
10163       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10164   }
10165
10166   /* check if element under the player changes from accessible to unaccessible
10167      (needed for special case of dropping element which then changes) */
10168   /* (must be checked after creating new element for walkable group elements) */
10169   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10170       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10171   {
10172     Bang(x, y);
10173
10174     return;
10175   }
10176
10177   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10178   if (new_element_is_player)
10179     RelocatePlayer(x, y, new_element);
10180
10181   if (is_change)
10182     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10183
10184   TestIfBadThingTouchesPlayer(x, y);
10185   TestIfPlayerTouchesCustomElement(x, y);
10186   TestIfElementTouchesCustomElement(x, y);
10187 }
10188
10189 static void CreateField(int x, int y, int element)
10190 {
10191   CreateFieldExt(x, y, element, FALSE);
10192 }
10193
10194 static void CreateElementFromChange(int x, int y, int element)
10195 {
10196   element = GET_VALID_RUNTIME_ELEMENT(element);
10197
10198   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10199   {
10200     int old_element = Feld[x][y];
10201
10202     /* prevent changed element from moving in same engine frame
10203        unless both old and new element can either fall or move */
10204     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10205         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10206       Stop[x][y] = TRUE;
10207   }
10208
10209   CreateFieldExt(x, y, element, TRUE);
10210 }
10211
10212 static boolean ChangeElement(int x, int y, int element, int page)
10213 {
10214   struct ElementInfo *ei = &element_info[element];
10215   struct ElementChangeInfo *change = &ei->change_page[page];
10216   int ce_value = CustomValue[x][y];
10217   int ce_score = ei->collect_score;
10218   int target_element;
10219   int old_element = Feld[x][y];
10220
10221   /* always use default change event to prevent running into a loop */
10222   if (ChangeEvent[x][y] == -1)
10223     ChangeEvent[x][y] = CE_DELAY;
10224
10225   if (ChangeEvent[x][y] == CE_DELAY)
10226   {
10227     /* reset actual trigger element, trigger player and action element */
10228     change->actual_trigger_element = EL_EMPTY;
10229     change->actual_trigger_player = EL_EMPTY;
10230     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10231     change->actual_trigger_side = CH_SIDE_NONE;
10232     change->actual_trigger_ce_value = 0;
10233     change->actual_trigger_ce_score = 0;
10234   }
10235
10236   /* do not change elements more than a specified maximum number of changes */
10237   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10238     return FALSE;
10239
10240   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10241
10242   if (change->explode)
10243   {
10244     Bang(x, y);
10245
10246     return TRUE;
10247   }
10248
10249   if (change->use_target_content)
10250   {
10251     boolean complete_replace = TRUE;
10252     boolean can_replace[3][3];
10253     int xx, yy;
10254
10255     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10256     {
10257       boolean is_empty;
10258       boolean is_walkable;
10259       boolean is_diggable;
10260       boolean is_collectible;
10261       boolean is_removable;
10262       boolean is_destructible;
10263       int ex = x + xx - 1;
10264       int ey = y + yy - 1;
10265       int content_element = change->target_content.e[xx][yy];
10266       int e;
10267
10268       can_replace[xx][yy] = TRUE;
10269
10270       if (ex == x && ey == y)   /* do not check changing element itself */
10271         continue;
10272
10273       if (content_element == EL_EMPTY_SPACE)
10274       {
10275         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10276
10277         continue;
10278       }
10279
10280       if (!IN_LEV_FIELD(ex, ey))
10281       {
10282         can_replace[xx][yy] = FALSE;
10283         complete_replace = FALSE;
10284
10285         continue;
10286       }
10287
10288       e = Feld[ex][ey];
10289
10290       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10291         e = MovingOrBlocked2Element(ex, ey);
10292
10293       is_empty = (IS_FREE(ex, ey) ||
10294                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10295
10296       is_walkable     = (is_empty || IS_WALKABLE(e));
10297       is_diggable     = (is_empty || IS_DIGGABLE(e));
10298       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10299       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10300       is_removable    = (is_diggable || is_collectible);
10301
10302       can_replace[xx][yy] =
10303         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10304           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10305           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10306           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10307           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10308           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10309          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10310
10311       if (!can_replace[xx][yy])
10312         complete_replace = FALSE;
10313     }
10314
10315     if (!change->only_if_complete || complete_replace)
10316     {
10317       boolean something_has_changed = FALSE;
10318
10319       if (change->only_if_complete && change->use_random_replace &&
10320           RND(100) < change->random_percentage)
10321         return FALSE;
10322
10323       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10324       {
10325         int ex = x + xx - 1;
10326         int ey = y + yy - 1;
10327         int content_element;
10328
10329         if (can_replace[xx][yy] && (!change->use_random_replace ||
10330                                     RND(100) < change->random_percentage))
10331         {
10332           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10333             RemoveMovingField(ex, ey);
10334
10335           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10336
10337           content_element = change->target_content.e[xx][yy];
10338           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10339                                               ce_value, ce_score);
10340
10341           CreateElementFromChange(ex, ey, target_element);
10342
10343           something_has_changed = TRUE;
10344
10345           /* for symmetry reasons, freeze newly created border elements */
10346           if (ex != x || ey != y)
10347             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10348         }
10349       }
10350
10351       if (something_has_changed)
10352       {
10353         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10354         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10355       }
10356     }
10357   }
10358   else
10359   {
10360     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10361                                         ce_value, ce_score);
10362
10363     if (element == EL_DIAGONAL_GROWING ||
10364         element == EL_DIAGONAL_SHRINKING)
10365     {
10366       target_element = Store[x][y];
10367
10368       Store[x][y] = EL_EMPTY;
10369     }
10370
10371     CreateElementFromChange(x, y, target_element);
10372
10373     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10374     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10375   }
10376
10377   /* this uses direct change before indirect change */
10378   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10379
10380   return TRUE;
10381 }
10382
10383 static void HandleElementChange(int x, int y, int page)
10384 {
10385   int element = MovingOrBlocked2Element(x, y);
10386   struct ElementInfo *ei = &element_info[element];
10387   struct ElementChangeInfo *change = &ei->change_page[page];
10388   boolean handle_action_before_change = FALSE;
10389
10390 #ifdef DEBUG
10391   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10392       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10393   {
10394     printf("\n\n");
10395     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10396            x, y, element, element_info[element].token_name);
10397     printf("HandleElementChange(): This should never happen!\n");
10398     printf("\n\n");
10399   }
10400 #endif
10401
10402   /* this can happen with classic bombs on walkable, changing elements */
10403   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10404   {
10405     return;
10406   }
10407
10408   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10409   {
10410     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10411
10412     if (change->can_change)
10413     {
10414       /* !!! not clear why graphic animation should be reset at all here !!! */
10415       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10416       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10417
10418       /*
10419         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10420
10421         When using an animation frame delay of 1 (this only happens with
10422         "sp_zonk.moving.left/right" in the classic graphics), the default
10423         (non-moving) animation shows wrong animation frames (while the
10424         moving animation, like "sp_zonk.moving.left/right", is correct,
10425         so this graphical bug never shows up with the classic graphics).
10426         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10427         be drawn instead of the correct frames 0,1,2,3. This is caused by
10428         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10429         an element change: First when the change delay ("ChangeDelay[][]")
10430         counter has reached zero after decrementing, then a second time in
10431         the next frame (after "GfxFrame[][]" was already incremented) when
10432         "ChangeDelay[][]" is reset to the initial delay value again.
10433
10434         This causes frame 0 to be drawn twice, while the last frame won't
10435         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10436
10437         As some animations may already be cleverly designed around this bug
10438         (at least the "Snake Bite" snake tail animation does this), it cannot
10439         simply be fixed here without breaking such existing animations.
10440         Unfortunately, it cannot easily be detected if a graphics set was
10441         designed "before" or "after" the bug was fixed. As a workaround,
10442         a new graphics set option "game.graphics_engine_version" was added
10443         to be able to specify the game's major release version for which the
10444         graphics set was designed, which can then be used to decide if the
10445         bugfix should be used (version 4 and above) or not (version 3 or
10446         below, or if no version was specified at all, as with old sets).
10447
10448         (The wrong/fixed animation frames can be tested with the test level set
10449         "test_gfxframe" and level "000", which contains a specially prepared
10450         custom element at level position (x/y) == (11/9) which uses the zonk
10451         animation mentioned above. Using "game.graphics_engine_version: 4"
10452         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10453         This can also be seen from the debug output for this test element.)
10454       */
10455
10456       /* when a custom element is about to change (for example by change delay),
10457          do not reset graphic animation when the custom element is moving */
10458       if (game.graphics_engine_version < 4 &&
10459           !IS_MOVING(x, y))
10460       {
10461         ResetGfxAnimation(x, y);
10462         ResetRandomAnimationValue(x, y);
10463       }
10464
10465       if (change->pre_change_function)
10466         change->pre_change_function(x, y);
10467     }
10468   }
10469
10470   ChangeDelay[x][y]--;
10471
10472   if (ChangeDelay[x][y] != 0)           /* continue element change */
10473   {
10474     if (change->can_change)
10475     {
10476       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10477
10478       if (IS_ANIMATED(graphic))
10479         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10480
10481       if (change->change_function)
10482         change->change_function(x, y);
10483     }
10484   }
10485   else                                  /* finish element change */
10486   {
10487     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10488     {
10489       page = ChangePage[x][y];
10490       ChangePage[x][y] = -1;
10491
10492       change = &ei->change_page[page];
10493     }
10494
10495     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10496     {
10497       ChangeDelay[x][y] = 1;            /* try change after next move step */
10498       ChangePage[x][y] = page;          /* remember page to use for change */
10499
10500       return;
10501     }
10502
10503     /* special case: set new level random seed before changing element */
10504     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10505       handle_action_before_change = TRUE;
10506
10507     if (change->has_action && handle_action_before_change)
10508       ExecuteCustomElementAction(x, y, element, page);
10509
10510     if (change->can_change)
10511     {
10512       if (ChangeElement(x, y, element, page))
10513       {
10514         if (change->post_change_function)
10515           change->post_change_function(x, y);
10516       }
10517     }
10518
10519     if (change->has_action && !handle_action_before_change)
10520       ExecuteCustomElementAction(x, y, element, page);
10521   }
10522 }
10523
10524 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10525                                               int trigger_element,
10526                                               int trigger_event,
10527                                               int trigger_player,
10528                                               int trigger_side,
10529                                               int trigger_page)
10530 {
10531   boolean change_done_any = FALSE;
10532   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10533   int i;
10534
10535   if (!(trigger_events[trigger_element][trigger_event]))
10536     return FALSE;
10537
10538   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10539
10540   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10541   {
10542     int element = EL_CUSTOM_START + i;
10543     boolean change_done = FALSE;
10544     int p;
10545
10546     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10547         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10548       continue;
10549
10550     for (p = 0; p < element_info[element].num_change_pages; p++)
10551     {
10552       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10553
10554       if (change->can_change_or_has_action &&
10555           change->has_event[trigger_event] &&
10556           change->trigger_side & trigger_side &&
10557           change->trigger_player & trigger_player &&
10558           change->trigger_page & trigger_page_bits &&
10559           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10560       {
10561         change->actual_trigger_element = trigger_element;
10562         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10563         change->actual_trigger_player_bits = trigger_player;
10564         change->actual_trigger_side = trigger_side;
10565         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10566         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10567
10568         if ((change->can_change && !change_done) || change->has_action)
10569         {
10570           int x, y;
10571
10572           SCAN_PLAYFIELD(x, y)
10573           {
10574             if (Feld[x][y] == element)
10575             {
10576               if (change->can_change && !change_done)
10577               {
10578                 /* if element already changed in this frame, not only prevent
10579                    another element change (checked in ChangeElement()), but
10580                    also prevent additional element actions for this element */
10581
10582                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10583                     !level.use_action_after_change_bug)
10584                   continue;
10585
10586                 ChangeDelay[x][y] = 1;
10587                 ChangeEvent[x][y] = trigger_event;
10588
10589                 HandleElementChange(x, y, p);
10590               }
10591               else if (change->has_action)
10592               {
10593                 /* if element already changed in this frame, not only prevent
10594                    another element change (checked in ChangeElement()), but
10595                    also prevent additional element actions for this element */
10596
10597                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10598                     !level.use_action_after_change_bug)
10599                   continue;
10600
10601                 ExecuteCustomElementAction(x, y, element, p);
10602                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10603               }
10604             }
10605           }
10606
10607           if (change->can_change)
10608           {
10609             change_done = TRUE;
10610             change_done_any = TRUE;
10611           }
10612         }
10613       }
10614     }
10615   }
10616
10617   RECURSION_LOOP_DETECTION_END();
10618
10619   return change_done_any;
10620 }
10621
10622 static boolean CheckElementChangeExt(int x, int y,
10623                                      int element,
10624                                      int trigger_element,
10625                                      int trigger_event,
10626                                      int trigger_player,
10627                                      int trigger_side)
10628 {
10629   boolean change_done = FALSE;
10630   int p;
10631
10632   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10633       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10634     return FALSE;
10635
10636   if (Feld[x][y] == EL_BLOCKED)
10637   {
10638     Blocked2Moving(x, y, &x, &y);
10639     element = Feld[x][y];
10640   }
10641
10642   /* check if element has already changed or is about to change after moving */
10643   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10644        Feld[x][y] != element) ||
10645
10646       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10647        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10648         ChangePage[x][y] != -1)))
10649     return FALSE;
10650
10651   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10652
10653   for (p = 0; p < element_info[element].num_change_pages; p++)
10654   {
10655     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10656
10657     /* check trigger element for all events where the element that is checked
10658        for changing interacts with a directly adjacent element -- this is
10659        different to element changes that affect other elements to change on the
10660        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10661     boolean check_trigger_element =
10662       (trigger_event == CE_TOUCHING_X ||
10663        trigger_event == CE_HITTING_X ||
10664        trigger_event == CE_HIT_BY_X ||
10665        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10666
10667     if (change->can_change_or_has_action &&
10668         change->has_event[trigger_event] &&
10669         change->trigger_side & trigger_side &&
10670         change->trigger_player & trigger_player &&
10671         (!check_trigger_element ||
10672          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10673     {
10674       change->actual_trigger_element = trigger_element;
10675       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10676       change->actual_trigger_player_bits = trigger_player;
10677       change->actual_trigger_side = trigger_side;
10678       change->actual_trigger_ce_value = CustomValue[x][y];
10679       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10680
10681       /* special case: trigger element not at (x,y) position for some events */
10682       if (check_trigger_element)
10683       {
10684         static struct
10685         {
10686           int dx, dy;
10687         } move_xy[] =
10688           {
10689             {  0,  0 },
10690             { -1,  0 },
10691             { +1,  0 },
10692             {  0,  0 },
10693             {  0, -1 },
10694             {  0,  0 }, { 0, 0 }, { 0, 0 },
10695             {  0, +1 }
10696           };
10697
10698         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10699         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10700
10701         change->actual_trigger_ce_value = CustomValue[xx][yy];
10702         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10703       }
10704
10705       if (change->can_change && !change_done)
10706       {
10707         ChangeDelay[x][y] = 1;
10708         ChangeEvent[x][y] = trigger_event;
10709
10710         HandleElementChange(x, y, p);
10711
10712         change_done = TRUE;
10713       }
10714       else if (change->has_action)
10715       {
10716         ExecuteCustomElementAction(x, y, element, p);
10717         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10718       }
10719     }
10720   }
10721
10722   RECURSION_LOOP_DETECTION_END();
10723
10724   return change_done;
10725 }
10726
10727 static void PlayPlayerSound(struct PlayerInfo *player)
10728 {
10729   int jx = player->jx, jy = player->jy;
10730   int sound_element = player->artwork_element;
10731   int last_action = player->last_action_waiting;
10732   int action = player->action_waiting;
10733
10734   if (player->is_waiting)
10735   {
10736     if (action != last_action)
10737       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10738     else
10739       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10740   }
10741   else
10742   {
10743     if (action != last_action)
10744       StopSound(element_info[sound_element].sound[last_action]);
10745
10746     if (last_action == ACTION_SLEEPING)
10747       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10748   }
10749 }
10750
10751 static void PlayAllPlayersSound()
10752 {
10753   int i;
10754
10755   for (i = 0; i < MAX_PLAYERS; i++)
10756     if (stored_player[i].active)
10757       PlayPlayerSound(&stored_player[i]);
10758 }
10759
10760 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10761 {
10762   boolean last_waiting = player->is_waiting;
10763   int move_dir = player->MovDir;
10764
10765   player->dir_waiting = move_dir;
10766   player->last_action_waiting = player->action_waiting;
10767
10768   if (is_waiting)
10769   {
10770     if (!last_waiting)          /* not waiting -> waiting */
10771     {
10772       player->is_waiting = TRUE;
10773
10774       player->frame_counter_bored =
10775         FrameCounter +
10776         game.player_boring_delay_fixed +
10777         GetSimpleRandom(game.player_boring_delay_random);
10778       player->frame_counter_sleeping =
10779         FrameCounter +
10780         game.player_sleeping_delay_fixed +
10781         GetSimpleRandom(game.player_sleeping_delay_random);
10782
10783       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10784     }
10785
10786     if (game.player_sleeping_delay_fixed +
10787         game.player_sleeping_delay_random > 0 &&
10788         player->anim_delay_counter == 0 &&
10789         player->post_delay_counter == 0 &&
10790         FrameCounter >= player->frame_counter_sleeping)
10791       player->is_sleeping = TRUE;
10792     else if (game.player_boring_delay_fixed +
10793              game.player_boring_delay_random > 0 &&
10794              FrameCounter >= player->frame_counter_bored)
10795       player->is_bored = TRUE;
10796
10797     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10798                               player->is_bored ? ACTION_BORING :
10799                               ACTION_WAITING);
10800
10801     if (player->is_sleeping && player->use_murphy)
10802     {
10803       /* special case for sleeping Murphy when leaning against non-free tile */
10804
10805       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10806           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10807            !IS_MOVING(player->jx - 1, player->jy)))
10808         move_dir = MV_LEFT;
10809       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10810                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10811                 !IS_MOVING(player->jx + 1, player->jy)))
10812         move_dir = MV_RIGHT;
10813       else
10814         player->is_sleeping = FALSE;
10815
10816       player->dir_waiting = move_dir;
10817     }
10818
10819     if (player->is_sleeping)
10820     {
10821       if (player->num_special_action_sleeping > 0)
10822       {
10823         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10824         {
10825           int last_special_action = player->special_action_sleeping;
10826           int num_special_action = player->num_special_action_sleeping;
10827           int special_action =
10828             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10829              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10830              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10831              last_special_action + 1 : ACTION_SLEEPING);
10832           int special_graphic =
10833             el_act_dir2img(player->artwork_element, special_action, move_dir);
10834
10835           player->anim_delay_counter =
10836             graphic_info[special_graphic].anim_delay_fixed +
10837             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10838           player->post_delay_counter =
10839             graphic_info[special_graphic].post_delay_fixed +
10840             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10841
10842           player->special_action_sleeping = special_action;
10843         }
10844
10845         if (player->anim_delay_counter > 0)
10846         {
10847           player->action_waiting = player->special_action_sleeping;
10848           player->anim_delay_counter--;
10849         }
10850         else if (player->post_delay_counter > 0)
10851         {
10852           player->post_delay_counter--;
10853         }
10854       }
10855     }
10856     else if (player->is_bored)
10857     {
10858       if (player->num_special_action_bored > 0)
10859       {
10860         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10861         {
10862           int special_action =
10863             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10864           int special_graphic =
10865             el_act_dir2img(player->artwork_element, special_action, move_dir);
10866
10867           player->anim_delay_counter =
10868             graphic_info[special_graphic].anim_delay_fixed +
10869             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10870           player->post_delay_counter =
10871             graphic_info[special_graphic].post_delay_fixed +
10872             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10873
10874           player->special_action_bored = special_action;
10875         }
10876
10877         if (player->anim_delay_counter > 0)
10878         {
10879           player->action_waiting = player->special_action_bored;
10880           player->anim_delay_counter--;
10881         }
10882         else if (player->post_delay_counter > 0)
10883         {
10884           player->post_delay_counter--;
10885         }
10886       }
10887     }
10888   }
10889   else if (last_waiting)        /* waiting -> not waiting */
10890   {
10891     player->is_waiting = FALSE;
10892     player->is_bored = FALSE;
10893     player->is_sleeping = FALSE;
10894
10895     player->frame_counter_bored = -1;
10896     player->frame_counter_sleeping = -1;
10897
10898     player->anim_delay_counter = 0;
10899     player->post_delay_counter = 0;
10900
10901     player->dir_waiting = player->MovDir;
10902     player->action_waiting = ACTION_DEFAULT;
10903
10904     player->special_action_bored = ACTION_DEFAULT;
10905     player->special_action_sleeping = ACTION_DEFAULT;
10906   }
10907 }
10908
10909 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10910 {
10911   if ((!player->is_moving  && player->was_moving) ||
10912       (player->MovPos == 0 && player->was_moving) ||
10913       (player->is_snapping && !player->was_snapping) ||
10914       (player->is_dropping && !player->was_dropping))
10915   {
10916     if (!CheckSaveEngineSnapshotToList())
10917       return;
10918
10919     player->was_moving = FALSE;
10920     player->was_snapping = TRUE;
10921     player->was_dropping = TRUE;
10922   }
10923   else
10924   {
10925     if (player->is_moving)
10926       player->was_moving = TRUE;
10927
10928     if (!player->is_snapping)
10929       player->was_snapping = FALSE;
10930
10931     if (!player->is_dropping)
10932       player->was_dropping = FALSE;
10933   }
10934 }
10935
10936 static void CheckSingleStepMode(struct PlayerInfo *player)
10937 {
10938   if (tape.single_step && tape.recording && !tape.pausing)
10939   {
10940     /* as it is called "single step mode", just return to pause mode when the
10941        player stopped moving after one tile (or never starts moving at all) */
10942     if (!player->is_moving &&
10943         !player->is_pushing &&
10944         !player->is_dropping_pressed)
10945     {
10946       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10947       SnapField(player, 0, 0);                  /* stop snapping */
10948     }
10949   }
10950
10951   CheckSaveEngineSnapshot(player);
10952 }
10953
10954 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10955 {
10956   int left      = player_action & JOY_LEFT;
10957   int right     = player_action & JOY_RIGHT;
10958   int up        = player_action & JOY_UP;
10959   int down      = player_action & JOY_DOWN;
10960   int button1   = player_action & JOY_BUTTON_1;
10961   int button2   = player_action & JOY_BUTTON_2;
10962   int dx        = (left ? -1 : right ? 1 : 0);
10963   int dy        = (up   ? -1 : down  ? 1 : 0);
10964
10965   if (!player->active || tape.pausing)
10966     return 0;
10967
10968   if (player_action)
10969   {
10970     if (button1)
10971       SnapField(player, dx, dy);
10972     else
10973     {
10974       if (button2)
10975         DropElement(player);
10976
10977       MovePlayer(player, dx, dy);
10978     }
10979
10980     CheckSingleStepMode(player);
10981
10982     SetPlayerWaiting(player, FALSE);
10983
10984     return player_action;
10985   }
10986   else
10987   {
10988     /* no actions for this player (no input at player's configured device) */
10989
10990     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10991     SnapField(player, 0, 0);
10992     CheckGravityMovementWhenNotMoving(player);
10993
10994     if (player->MovPos == 0)
10995       SetPlayerWaiting(player, TRUE);
10996
10997     if (player->MovPos == 0)    /* needed for tape.playing */
10998       player->is_moving = FALSE;
10999
11000     player->is_dropping = FALSE;
11001     player->is_dropping_pressed = FALSE;
11002     player->drop_pressed_delay = 0;
11003
11004     CheckSingleStepMode(player);
11005
11006     return 0;
11007   }
11008 }
11009
11010 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11011                                          byte *tape_action)
11012 {
11013   if (!tape.use_mouse)
11014     return;
11015
11016   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11017   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11018   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11019 }
11020
11021 static void SetTapeActionFromMouseAction(byte *tape_action,
11022                                          struct MouseActionInfo *mouse_action)
11023 {
11024   if (!tape.use_mouse)
11025     return;
11026
11027   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11028   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11029   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11030 }
11031
11032 static void CheckLevelTime()
11033 {
11034   int i;
11035
11036   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11037   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11038   {
11039     if (level.native_em_level->lev->home == 0)  /* all players at home */
11040     {
11041       PlayerWins(local_player);
11042
11043       AllPlayersGone = TRUE;
11044
11045       level.native_em_level->lev->home = -1;
11046     }
11047
11048     if (level.native_em_level->ply[0]->alive == 0 &&
11049         level.native_em_level->ply[1]->alive == 0 &&
11050         level.native_em_level->ply[2]->alive == 0 &&
11051         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11052       AllPlayersGone = TRUE;
11053   }
11054   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11055   {
11056     if (game_sp.LevelSolved &&
11057         !game_sp.GameOver)                              /* game won */
11058     {
11059       PlayerWins(local_player);
11060
11061       game_sp.GameOver = TRUE;
11062
11063       AllPlayersGone = TRUE;
11064     }
11065
11066     if (game_sp.GameOver)                               /* game lost */
11067       AllPlayersGone = TRUE;
11068   }
11069   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11070   {
11071     if (game_mm.level_solved &&
11072         !game_mm.game_over)                             /* game won */
11073     {
11074       PlayerWins(local_player);
11075
11076       game_mm.game_over = TRUE;
11077
11078       AllPlayersGone = TRUE;
11079     }
11080
11081     if (game_mm.game_over)                              /* game lost */
11082       AllPlayersGone = TRUE;
11083   }
11084
11085   if (TimeFrames >= FRAMES_PER_SECOND)
11086   {
11087     TimeFrames = 0;
11088     TapeTime++;
11089
11090     for (i = 0; i < MAX_PLAYERS; i++)
11091     {
11092       struct PlayerInfo *player = &stored_player[i];
11093
11094       if (SHIELD_ON(player))
11095       {
11096         player->shield_normal_time_left--;
11097
11098         if (player->shield_deadly_time_left > 0)
11099           player->shield_deadly_time_left--;
11100       }
11101     }
11102
11103     if (!local_player->LevelSolved && !level.use_step_counter)
11104     {
11105       TimePlayed++;
11106
11107       if (TimeLeft > 0)
11108       {
11109         TimeLeft--;
11110
11111         if (TimeLeft <= 10 && setup.time_limit)
11112           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11113
11114         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11115            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11116
11117         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11118
11119         if (!TimeLeft && setup.time_limit)
11120         {
11121           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11122             level.native_em_level->lev->killed_out_of_time = TRUE;
11123           else
11124             for (i = 0; i < MAX_PLAYERS; i++)
11125               KillPlayer(&stored_player[i]);
11126         }
11127       }
11128       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11129       {
11130         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11131       }
11132
11133       level.native_em_level->lev->time =
11134         (game.no_time_limit ? TimePlayed : TimeLeft);
11135     }
11136
11137     if (tape.recording || tape.playing)
11138       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11139   }
11140
11141   if (tape.recording || tape.playing)
11142     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11143
11144   UpdateAndDisplayGameControlValues();
11145 }
11146
11147 void AdvanceFrameAndPlayerCounters(int player_nr)
11148 {
11149   int i;
11150
11151   /* advance frame counters (global frame counter and time frame counter) */
11152   FrameCounter++;
11153   TimeFrames++;
11154
11155   /* advance player counters (counters for move delay, move animation etc.) */
11156   for (i = 0; i < MAX_PLAYERS; i++)
11157   {
11158     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11159     int move_delay_value = stored_player[i].move_delay_value;
11160     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11161
11162     if (!advance_player_counters)       /* not all players may be affected */
11163       continue;
11164
11165     if (move_frames == 0)       /* less than one move per game frame */
11166     {
11167       int stepsize = TILEX / move_delay_value;
11168       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11169       int count = (stored_player[i].is_moving ?
11170                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11171
11172       if (count % delay == 0)
11173         move_frames = 1;
11174     }
11175
11176     stored_player[i].Frame += move_frames;
11177
11178     if (stored_player[i].MovPos != 0)
11179       stored_player[i].StepFrame += move_frames;
11180
11181     if (stored_player[i].move_delay > 0)
11182       stored_player[i].move_delay--;
11183
11184     /* due to bugs in previous versions, counter must count up, not down */
11185     if (stored_player[i].push_delay != -1)
11186       stored_player[i].push_delay++;
11187
11188     if (stored_player[i].drop_delay > 0)
11189       stored_player[i].drop_delay--;
11190
11191     if (stored_player[i].is_dropping_pressed)
11192       stored_player[i].drop_pressed_delay++;
11193   }
11194 }
11195
11196 void StartGameActions(boolean init_network_game, boolean record_tape,
11197                       int random_seed)
11198 {
11199   unsigned int new_random_seed = InitRND(random_seed);
11200
11201   if (record_tape)
11202     TapeStartRecording(new_random_seed);
11203
11204   if (init_network_game)
11205   {
11206     SendToServer_StartPlaying();
11207
11208     return;
11209   }
11210
11211   InitGame();
11212 }
11213
11214 void GameActionsExt()
11215 {
11216 #if 0
11217   static unsigned int game_frame_delay = 0;
11218 #endif
11219   unsigned int game_frame_delay_value;
11220   byte *recorded_player_action;
11221   byte summarized_player_action = 0;
11222   byte tape_action[MAX_PLAYERS];
11223   int i;
11224
11225   /* detect endless loops, caused by custom element programming */
11226   if (recursion_loop_detected && recursion_loop_depth == 0)
11227   {
11228     char *message = getStringCat3("Internal Error! Element ",
11229                                   EL_NAME(recursion_loop_element),
11230                                   " caused endless loop! Quit the game?");
11231
11232     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11233           EL_NAME(recursion_loop_element));
11234
11235     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11236
11237     recursion_loop_detected = FALSE;    /* if game should be continued */
11238
11239     free(message);
11240
11241     return;
11242   }
11243
11244   if (game.restart_level)
11245     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11246
11247   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11248   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11249   {
11250     if (level.native_em_level->lev->home == 0)  /* all players at home */
11251     {
11252       PlayerWins(local_player);
11253
11254       AllPlayersGone = TRUE;
11255
11256       level.native_em_level->lev->home = -1;
11257     }
11258
11259     if (level.native_em_level->ply[0]->alive == 0 &&
11260         level.native_em_level->ply[1]->alive == 0 &&
11261         level.native_em_level->ply[2]->alive == 0 &&
11262         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11263       AllPlayersGone = TRUE;
11264   }
11265   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11266   {
11267     if (game_sp.LevelSolved &&
11268         !game_sp.GameOver)                              /* game won */
11269     {
11270       PlayerWins(local_player);
11271
11272       game_sp.GameOver = TRUE;
11273
11274       AllPlayersGone = TRUE;
11275     }
11276
11277     if (game_sp.GameOver)                               /* game lost */
11278       AllPlayersGone = TRUE;
11279   }
11280   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11281   {
11282     if (game_mm.level_solved &&
11283         !game_mm.game_over)                             /* game won */
11284     {
11285       PlayerWins(local_player);
11286
11287       game_mm.game_over = TRUE;
11288
11289       AllPlayersGone = TRUE;
11290     }
11291
11292     if (game_mm.game_over)                              /* game lost */
11293       AllPlayersGone = TRUE;
11294   }
11295
11296   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11297     GameWon();
11298
11299   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11300     TapeStop();
11301
11302   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11303     return;
11304
11305   game_frame_delay_value =
11306     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11307
11308   if (tape.playing && tape.warp_forward && !tape.pausing)
11309     game_frame_delay_value = 0;
11310
11311   SetVideoFrameDelay(game_frame_delay_value);
11312
11313 #if 0
11314 #if 0
11315   /* ---------- main game synchronization point ---------- */
11316
11317   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11318
11319   printf("::: skip == %d\n", skip);
11320
11321 #else
11322   /* ---------- main game synchronization point ---------- */
11323
11324   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11325 #endif
11326 #endif
11327
11328   if (network_playing && !network_player_action_received)
11329   {
11330     /* try to get network player actions in time */
11331
11332     /* last chance to get network player actions without main loop delay */
11333     HandleNetworking();
11334
11335     /* game was quit by network peer */
11336     if (game_status != GAME_MODE_PLAYING)
11337       return;
11338
11339     if (!network_player_action_received)
11340       return;           /* failed to get network player actions in time */
11341
11342     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11343   }
11344
11345   if (tape.pausing)
11346     return;
11347
11348   /* at this point we know that we really continue executing the game */
11349
11350   network_player_action_received = FALSE;
11351
11352   /* when playing tape, read previously recorded player input from tape data */
11353   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11354
11355   local_player->effective_mouse_action = local_player->mouse_action;
11356
11357   if (recorded_player_action != NULL)
11358     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11359                                  recorded_player_action);
11360
11361   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11362   if (tape.pausing)
11363     return;
11364
11365   if (tape.set_centered_player)
11366   {
11367     game.centered_player_nr_next = tape.centered_player_nr_next;
11368     game.set_centered_player = TRUE;
11369   }
11370
11371   for (i = 0; i < MAX_PLAYERS; i++)
11372   {
11373     summarized_player_action |= stored_player[i].action;
11374
11375     if (!network_playing && (game.team_mode || tape.playing))
11376       stored_player[i].effective_action = stored_player[i].action;
11377   }
11378
11379   if (network_playing)
11380     SendToServer_MovePlayer(summarized_player_action);
11381
11382   // summarize all actions at local players mapped input device position
11383   // (this allows using different input devices in single player mode)
11384   if (!network.enabled && !game.team_mode)
11385     stored_player[map_player_action[local_player->index_nr]].effective_action =
11386       summarized_player_action;
11387
11388   if (tape.recording &&
11389       setup.team_mode &&
11390       setup.input_on_focus &&
11391       game.centered_player_nr != -1)
11392   {
11393     for (i = 0; i < MAX_PLAYERS; i++)
11394       stored_player[i].effective_action =
11395         (i == game.centered_player_nr ? summarized_player_action : 0);
11396   }
11397
11398   if (recorded_player_action != NULL)
11399     for (i = 0; i < MAX_PLAYERS; i++)
11400       stored_player[i].effective_action = recorded_player_action[i];
11401
11402   for (i = 0; i < MAX_PLAYERS; i++)
11403   {
11404     tape_action[i] = stored_player[i].effective_action;
11405
11406     /* (this may happen in the RND game engine if a player was not present on
11407        the playfield on level start, but appeared later from a custom element */
11408     if (setup.team_mode &&
11409         tape.recording &&
11410         tape_action[i] &&
11411         !tape.player_participates[i])
11412       tape.player_participates[i] = TRUE;
11413   }
11414
11415   SetTapeActionFromMouseAction(tape_action,
11416                                &local_player->effective_mouse_action);
11417
11418   /* only record actions from input devices, but not programmed actions */
11419   if (tape.recording)
11420     TapeRecordAction(tape_action);
11421
11422 #if USE_NEW_PLAYER_ASSIGNMENTS
11423   // !!! also map player actions in single player mode !!!
11424   // if (game.team_mode)
11425   if (1)
11426   {
11427     byte mapped_action[MAX_PLAYERS];
11428
11429 #if DEBUG_PLAYER_ACTIONS
11430     printf(":::");
11431     for (i = 0; i < MAX_PLAYERS; i++)
11432       printf(" %d, ", stored_player[i].effective_action);
11433 #endif
11434
11435     for (i = 0; i < MAX_PLAYERS; i++)
11436       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11437
11438     for (i = 0; i < MAX_PLAYERS; i++)
11439       stored_player[i].effective_action = mapped_action[i];
11440
11441 #if DEBUG_PLAYER_ACTIONS
11442     printf(" =>");
11443     for (i = 0; i < MAX_PLAYERS; i++)
11444       printf(" %d, ", stored_player[i].effective_action);
11445     printf("\n");
11446 #endif
11447   }
11448 #if DEBUG_PLAYER_ACTIONS
11449   else
11450   {
11451     printf(":::");
11452     for (i = 0; i < MAX_PLAYERS; i++)
11453       printf(" %d, ", stored_player[i].effective_action);
11454     printf("\n");
11455   }
11456 #endif
11457 #endif
11458
11459   for (i = 0; i < MAX_PLAYERS; i++)
11460   {
11461     // allow engine snapshot in case of changed movement attempt
11462     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11463         (stored_player[i].effective_action & KEY_MOTION))
11464       game.snapshot.changed_action = TRUE;
11465
11466     // allow engine snapshot in case of snapping/dropping attempt
11467     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11468         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11469       game.snapshot.changed_action = TRUE;
11470
11471     game.snapshot.last_action[i] = stored_player[i].effective_action;
11472   }
11473
11474   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11475   {
11476     GameActions_EM_Main();
11477   }
11478   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11479   {
11480     GameActions_SP_Main();
11481   }
11482   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11483   {
11484     GameActions_MM_Main();
11485   }
11486   else
11487   {
11488     GameActions_RND_Main();
11489   }
11490
11491   BlitScreenToBitmap(backbuffer);
11492
11493   CheckLevelTime();
11494
11495   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11496
11497   if (global.show_frames_per_second)
11498   {
11499     static unsigned int fps_counter = 0;
11500     static int fps_frames = 0;
11501     unsigned int fps_delay_ms = Counter() - fps_counter;
11502
11503     fps_frames++;
11504
11505     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11506     {
11507       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11508
11509       fps_frames = 0;
11510       fps_counter = Counter();
11511
11512       /* always draw FPS to screen after FPS value was updated */
11513       redraw_mask |= REDRAW_FPS;
11514     }
11515
11516     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11517     if (GetDrawDeactivationMask() == REDRAW_NONE)
11518       redraw_mask |= REDRAW_FPS;
11519   }
11520 }
11521
11522 static void GameActions_CheckSaveEngineSnapshot()
11523 {
11524   if (!game.snapshot.save_snapshot)
11525     return;
11526
11527   // clear flag for saving snapshot _before_ saving snapshot
11528   game.snapshot.save_snapshot = FALSE;
11529
11530   SaveEngineSnapshotToList();
11531 }
11532
11533 void GameActions()
11534 {
11535   GameActionsExt();
11536
11537   GameActions_CheckSaveEngineSnapshot();
11538 }
11539
11540 void GameActions_EM_Main()
11541 {
11542   byte effective_action[MAX_PLAYERS];
11543   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11544   int i;
11545
11546   for (i = 0; i < MAX_PLAYERS; i++)
11547     effective_action[i] = stored_player[i].effective_action;
11548
11549   GameActions_EM(effective_action, warp_mode);
11550 }
11551
11552 void GameActions_SP_Main()
11553 {
11554   byte effective_action[MAX_PLAYERS];
11555   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11556   int i;
11557
11558   for (i = 0; i < MAX_PLAYERS; i++)
11559     effective_action[i] = stored_player[i].effective_action;
11560
11561   GameActions_SP(effective_action, warp_mode);
11562
11563   for (i = 0; i < MAX_PLAYERS; i++)
11564   {
11565     if (stored_player[i].force_dropping)
11566       stored_player[i].action |= KEY_BUTTON_DROP;
11567
11568     stored_player[i].force_dropping = FALSE;
11569   }
11570 }
11571
11572 void GameActions_MM_Main()
11573 {
11574   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11575
11576   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11577 }
11578
11579 void GameActions_RND_Main()
11580 {
11581   GameActions_RND();
11582 }
11583
11584 void GameActions_RND()
11585 {
11586   int magic_wall_x = 0, magic_wall_y = 0;
11587   int i, x, y, element, graphic, last_gfx_frame;
11588
11589   InitPlayfieldScanModeVars();
11590
11591   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11592   {
11593     SCAN_PLAYFIELD(x, y)
11594     {
11595       ChangeCount[x][y] = 0;
11596       ChangeEvent[x][y] = -1;
11597     }
11598   }
11599
11600   if (game.set_centered_player)
11601   {
11602     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11603
11604     /* switching to "all players" only possible if all players fit to screen */
11605     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11606     {
11607       game.centered_player_nr_next = game.centered_player_nr;
11608       game.set_centered_player = FALSE;
11609     }
11610
11611     /* do not switch focus to non-existing (or non-active) player */
11612     if (game.centered_player_nr_next >= 0 &&
11613         !stored_player[game.centered_player_nr_next].active)
11614     {
11615       game.centered_player_nr_next = game.centered_player_nr;
11616       game.set_centered_player = FALSE;
11617     }
11618   }
11619
11620   if (game.set_centered_player &&
11621       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11622   {
11623     int sx, sy;
11624
11625     if (game.centered_player_nr_next == -1)
11626     {
11627       setScreenCenteredToAllPlayers(&sx, &sy);
11628     }
11629     else
11630     {
11631       sx = stored_player[game.centered_player_nr_next].jx;
11632       sy = stored_player[game.centered_player_nr_next].jy;
11633     }
11634
11635     game.centered_player_nr = game.centered_player_nr_next;
11636     game.set_centered_player = FALSE;
11637
11638     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11639     DrawGameDoorValues();
11640   }
11641
11642   for (i = 0; i < MAX_PLAYERS; i++)
11643   {
11644     int actual_player_action = stored_player[i].effective_action;
11645
11646 #if 1
11647     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11648        - rnd_equinox_tetrachloride 048
11649        - rnd_equinox_tetrachloride_ii 096
11650        - rnd_emanuel_schmieg 002
11651        - doctor_sloan_ww 001, 020
11652     */
11653     if (stored_player[i].MovPos == 0)
11654       CheckGravityMovement(&stored_player[i]);
11655 #endif
11656
11657     /* overwrite programmed action with tape action */
11658     if (stored_player[i].programmed_action)
11659       actual_player_action = stored_player[i].programmed_action;
11660
11661     PlayerActions(&stored_player[i], actual_player_action);
11662
11663     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11664   }
11665
11666   ScrollScreen(NULL, SCROLL_GO_ON);
11667
11668   /* for backwards compatibility, the following code emulates a fixed bug that
11669      occured when pushing elements (causing elements that just made their last
11670      pushing step to already (if possible) make their first falling step in the
11671      same game frame, which is bad); this code is also needed to use the famous
11672      "spring push bug" which is used in older levels and might be wanted to be
11673      used also in newer levels, but in this case the buggy pushing code is only
11674      affecting the "spring" element and no other elements */
11675
11676   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11677   {
11678     for (i = 0; i < MAX_PLAYERS; i++)
11679     {
11680       struct PlayerInfo *player = &stored_player[i];
11681       int x = player->jx;
11682       int y = player->jy;
11683
11684       if (player->active && player->is_pushing && player->is_moving &&
11685           IS_MOVING(x, y) &&
11686           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11687            Feld[x][y] == EL_SPRING))
11688       {
11689         ContinueMoving(x, y);
11690
11691         /* continue moving after pushing (this is actually a bug) */
11692         if (!IS_MOVING(x, y))
11693           Stop[x][y] = FALSE;
11694       }
11695     }
11696   }
11697
11698   SCAN_PLAYFIELD(x, y)
11699   {
11700     ChangeCount[x][y] = 0;
11701     ChangeEvent[x][y] = -1;
11702
11703     /* this must be handled before main playfield loop */
11704     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11705     {
11706       MovDelay[x][y]--;
11707       if (MovDelay[x][y] <= 0)
11708         RemoveField(x, y);
11709     }
11710
11711     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11712     {
11713       MovDelay[x][y]--;
11714       if (MovDelay[x][y] <= 0)
11715       {
11716         RemoveField(x, y);
11717         TEST_DrawLevelField(x, y);
11718
11719         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11720       }
11721     }
11722
11723 #if DEBUG
11724     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11725     {
11726       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11727       printf("GameActions(): This should never happen!\n");
11728
11729       ChangePage[x][y] = -1;
11730     }
11731 #endif
11732
11733     Stop[x][y] = FALSE;
11734     if (WasJustMoving[x][y] > 0)
11735       WasJustMoving[x][y]--;
11736     if (WasJustFalling[x][y] > 0)
11737       WasJustFalling[x][y]--;
11738     if (CheckCollision[x][y] > 0)
11739       CheckCollision[x][y]--;
11740     if (CheckImpact[x][y] > 0)
11741       CheckImpact[x][y]--;
11742
11743     GfxFrame[x][y]++;
11744
11745     /* reset finished pushing action (not done in ContinueMoving() to allow
11746        continuous pushing animation for elements with zero push delay) */
11747     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11748     {
11749       ResetGfxAnimation(x, y);
11750       TEST_DrawLevelField(x, y);
11751     }
11752
11753 #if DEBUG
11754     if (IS_BLOCKED(x, y))
11755     {
11756       int oldx, oldy;
11757
11758       Blocked2Moving(x, y, &oldx, &oldy);
11759       if (!IS_MOVING(oldx, oldy))
11760       {
11761         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11762         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11763         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11764         printf("GameActions(): This should never happen!\n");
11765       }
11766     }
11767 #endif
11768   }
11769
11770   SCAN_PLAYFIELD(x, y)
11771   {
11772     element = Feld[x][y];
11773     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11774     last_gfx_frame = GfxFrame[x][y];
11775
11776     ResetGfxFrame(x, y);
11777
11778     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11779       DrawLevelGraphicAnimation(x, y, graphic);
11780
11781     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11782         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11783       ResetRandomAnimationValue(x, y);
11784
11785     SetRandomAnimationValue(x, y);
11786
11787     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11788
11789     if (IS_INACTIVE(element))
11790     {
11791       if (IS_ANIMATED(graphic))
11792         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11793
11794       continue;
11795     }
11796
11797     /* this may take place after moving, so 'element' may have changed */
11798     if (IS_CHANGING(x, y) &&
11799         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11800     {
11801       int page = element_info[element].event_page_nr[CE_DELAY];
11802
11803       HandleElementChange(x, y, page);
11804
11805       element = Feld[x][y];
11806       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11807     }
11808
11809     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11810     {
11811       StartMoving(x, y);
11812
11813       element = Feld[x][y];
11814       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11815
11816       if (IS_ANIMATED(graphic) &&
11817           !IS_MOVING(x, y) &&
11818           !Stop[x][y])
11819         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11820
11821       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11822         TEST_DrawTwinkleOnField(x, y);
11823     }
11824     else if (element == EL_ACID)
11825     {
11826       if (!Stop[x][y])
11827         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11828     }
11829     else if ((element == EL_EXIT_OPEN ||
11830               element == EL_EM_EXIT_OPEN ||
11831               element == EL_SP_EXIT_OPEN ||
11832               element == EL_STEEL_EXIT_OPEN ||
11833               element == EL_EM_STEEL_EXIT_OPEN ||
11834               element == EL_SP_TERMINAL ||
11835               element == EL_SP_TERMINAL_ACTIVE ||
11836               element == EL_EXTRA_TIME ||
11837               element == EL_SHIELD_NORMAL ||
11838               element == EL_SHIELD_DEADLY) &&
11839              IS_ANIMATED(graphic))
11840       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11841     else if (IS_MOVING(x, y))
11842       ContinueMoving(x, y);
11843     else if (IS_ACTIVE_BOMB(element))
11844       CheckDynamite(x, y);
11845     else if (element == EL_AMOEBA_GROWING)
11846       AmoebeWaechst(x, y);
11847     else if (element == EL_AMOEBA_SHRINKING)
11848       AmoebaDisappearing(x, y);
11849
11850 #if !USE_NEW_AMOEBA_CODE
11851     else if (IS_AMOEBALIVE(element))
11852       AmoebeAbleger(x, y);
11853 #endif
11854
11855     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11856       Life(x, y);
11857     else if (element == EL_EXIT_CLOSED)
11858       CheckExit(x, y);
11859     else if (element == EL_EM_EXIT_CLOSED)
11860       CheckExitEM(x, y);
11861     else if (element == EL_STEEL_EXIT_CLOSED)
11862       CheckExitSteel(x, y);
11863     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11864       CheckExitSteelEM(x, y);
11865     else if (element == EL_SP_EXIT_CLOSED)
11866       CheckExitSP(x, y);
11867     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11868              element == EL_EXPANDABLE_STEELWALL_GROWING)
11869       MauerWaechst(x, y);
11870     else if (element == EL_EXPANDABLE_WALL ||
11871              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11872              element == EL_EXPANDABLE_WALL_VERTICAL ||
11873              element == EL_EXPANDABLE_WALL_ANY ||
11874              element == EL_BD_EXPANDABLE_WALL)
11875       MauerAbleger(x, y);
11876     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11877              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11878              element == EL_EXPANDABLE_STEELWALL_ANY)
11879       MauerAblegerStahl(x, y);
11880     else if (element == EL_FLAMES)
11881       CheckForDragon(x, y);
11882     else if (element == EL_EXPLOSION)
11883       ; /* drawing of correct explosion animation is handled separately */
11884     else if (element == EL_ELEMENT_SNAPPING ||
11885              element == EL_DIAGONAL_SHRINKING ||
11886              element == EL_DIAGONAL_GROWING)
11887     {
11888       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11889
11890       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11891     }
11892     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11893       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11894
11895     if (IS_BELT_ACTIVE(element))
11896       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11897
11898     if (game.magic_wall_active)
11899     {
11900       int jx = local_player->jx, jy = local_player->jy;
11901
11902       /* play the element sound at the position nearest to the player */
11903       if ((element == EL_MAGIC_WALL_FULL ||
11904            element == EL_MAGIC_WALL_ACTIVE ||
11905            element == EL_MAGIC_WALL_EMPTYING ||
11906            element == EL_BD_MAGIC_WALL_FULL ||
11907            element == EL_BD_MAGIC_WALL_ACTIVE ||
11908            element == EL_BD_MAGIC_WALL_EMPTYING ||
11909            element == EL_DC_MAGIC_WALL_FULL ||
11910            element == EL_DC_MAGIC_WALL_ACTIVE ||
11911            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11912           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11913       {
11914         magic_wall_x = x;
11915         magic_wall_y = y;
11916       }
11917     }
11918   }
11919
11920 #if USE_NEW_AMOEBA_CODE
11921   /* new experimental amoeba growth stuff */
11922   if (!(FrameCounter % 8))
11923   {
11924     static unsigned int random = 1684108901;
11925
11926     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11927     {
11928       x = RND(lev_fieldx);
11929       y = RND(lev_fieldy);
11930       element = Feld[x][y];
11931
11932       if (!IS_PLAYER(x,y) &&
11933           (element == EL_EMPTY ||
11934            CAN_GROW_INTO(element) ||
11935            element == EL_QUICKSAND_EMPTY ||
11936            element == EL_QUICKSAND_FAST_EMPTY ||
11937            element == EL_ACID_SPLASH_LEFT ||
11938            element == EL_ACID_SPLASH_RIGHT))
11939       {
11940         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11941             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11942             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11943             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11944           Feld[x][y] = EL_AMOEBA_DROP;
11945       }
11946
11947       random = random * 129 + 1;
11948     }
11949   }
11950 #endif
11951
11952   game.explosions_delayed = FALSE;
11953
11954   SCAN_PLAYFIELD(x, y)
11955   {
11956     element = Feld[x][y];
11957
11958     if (ExplodeField[x][y])
11959       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11960     else if (element == EL_EXPLOSION)
11961       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11962
11963     ExplodeField[x][y] = EX_TYPE_NONE;
11964   }
11965
11966   game.explosions_delayed = TRUE;
11967
11968   if (game.magic_wall_active)
11969   {
11970     if (!(game.magic_wall_time_left % 4))
11971     {
11972       int element = Feld[magic_wall_x][magic_wall_y];
11973
11974       if (element == EL_BD_MAGIC_WALL_FULL ||
11975           element == EL_BD_MAGIC_WALL_ACTIVE ||
11976           element == EL_BD_MAGIC_WALL_EMPTYING)
11977         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11978       else if (element == EL_DC_MAGIC_WALL_FULL ||
11979                element == EL_DC_MAGIC_WALL_ACTIVE ||
11980                element == EL_DC_MAGIC_WALL_EMPTYING)
11981         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11982       else
11983         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11984     }
11985
11986     if (game.magic_wall_time_left > 0)
11987     {
11988       game.magic_wall_time_left--;
11989
11990       if (!game.magic_wall_time_left)
11991       {
11992         SCAN_PLAYFIELD(x, y)
11993         {
11994           element = Feld[x][y];
11995
11996           if (element == EL_MAGIC_WALL_ACTIVE ||
11997               element == EL_MAGIC_WALL_FULL)
11998           {
11999             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12000             TEST_DrawLevelField(x, y);
12001           }
12002           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12003                    element == EL_BD_MAGIC_WALL_FULL)
12004           {
12005             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12006             TEST_DrawLevelField(x, y);
12007           }
12008           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12009                    element == EL_DC_MAGIC_WALL_FULL)
12010           {
12011             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12012             TEST_DrawLevelField(x, y);
12013           }
12014         }
12015
12016         game.magic_wall_active = FALSE;
12017       }
12018     }
12019   }
12020
12021   if (game.light_time_left > 0)
12022   {
12023     game.light_time_left--;
12024
12025     if (game.light_time_left == 0)
12026       RedrawAllLightSwitchesAndInvisibleElements();
12027   }
12028
12029   if (game.timegate_time_left > 0)
12030   {
12031     game.timegate_time_left--;
12032
12033     if (game.timegate_time_left == 0)
12034       CloseAllOpenTimegates();
12035   }
12036
12037   if (game.lenses_time_left > 0)
12038   {
12039     game.lenses_time_left--;
12040
12041     if (game.lenses_time_left == 0)
12042       RedrawAllInvisibleElementsForLenses();
12043   }
12044
12045   if (game.magnify_time_left > 0)
12046   {
12047     game.magnify_time_left--;
12048
12049     if (game.magnify_time_left == 0)
12050       RedrawAllInvisibleElementsForMagnifier();
12051   }
12052
12053   for (i = 0; i < MAX_PLAYERS; i++)
12054   {
12055     struct PlayerInfo *player = &stored_player[i];
12056
12057     if (SHIELD_ON(player))
12058     {
12059       if (player->shield_deadly_time_left)
12060         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12061       else if (player->shield_normal_time_left)
12062         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12063     }
12064   }
12065
12066 #if USE_DELAYED_GFX_REDRAW
12067   SCAN_PLAYFIELD(x, y)
12068   {
12069     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12070     {
12071       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12072          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12073
12074       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12075         DrawLevelField(x, y);
12076
12077       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12078         DrawLevelFieldCrumbled(x, y);
12079
12080       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12081         DrawLevelFieldCrumbledNeighbours(x, y);
12082
12083       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12084         DrawTwinkleOnField(x, y);
12085     }
12086
12087     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12088   }
12089 #endif
12090
12091   DrawAllPlayers();
12092   PlayAllPlayersSound();
12093
12094   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12095   {
12096     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12097
12098     local_player->show_envelope = 0;
12099   }
12100
12101   /* use random number generator in every frame to make it less predictable */
12102   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12103     RND(1);
12104 }
12105
12106 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12107 {
12108   int min_x = x, min_y = y, max_x = x, max_y = y;
12109   int i;
12110
12111   for (i = 0; i < MAX_PLAYERS; i++)
12112   {
12113     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12114
12115     if (!stored_player[i].active || &stored_player[i] == player)
12116       continue;
12117
12118     min_x = MIN(min_x, jx);
12119     min_y = MIN(min_y, jy);
12120     max_x = MAX(max_x, jx);
12121     max_y = MAX(max_y, jy);
12122   }
12123
12124   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12125 }
12126
12127 static boolean AllPlayersInVisibleScreen()
12128 {
12129   int i;
12130
12131   for (i = 0; i < MAX_PLAYERS; i++)
12132   {
12133     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12134
12135     if (!stored_player[i].active)
12136       continue;
12137
12138     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12139       return FALSE;
12140   }
12141
12142   return TRUE;
12143 }
12144
12145 void ScrollLevel(int dx, int dy)
12146 {
12147   int scroll_offset = 2 * TILEX_VAR;
12148   int x, y;
12149
12150   BlitBitmap(drawto_field, drawto_field,
12151              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12152              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12153              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12154              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12155              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12156              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12157
12158   if (dx != 0)
12159   {
12160     x = (dx == 1 ? BX1 : BX2);
12161     for (y = BY1; y <= BY2; y++)
12162       DrawScreenField(x, y);
12163   }
12164
12165   if (dy != 0)
12166   {
12167     y = (dy == 1 ? BY1 : BY2);
12168     for (x = BX1; x <= BX2; x++)
12169       DrawScreenField(x, y);
12170   }
12171
12172   redraw_mask |= REDRAW_FIELD;
12173 }
12174
12175 static boolean canFallDown(struct PlayerInfo *player)
12176 {
12177   int jx = player->jx, jy = player->jy;
12178
12179   return (IN_LEV_FIELD(jx, jy + 1) &&
12180           (IS_FREE(jx, jy + 1) ||
12181            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12182           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12183           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12184 }
12185
12186 static boolean canPassField(int x, int y, int move_dir)
12187 {
12188   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12189   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12190   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12191   int nextx = x + dx;
12192   int nexty = y + dy;
12193   int element = Feld[x][y];
12194
12195   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12196           !CAN_MOVE(element) &&
12197           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12198           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12199           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12200 }
12201
12202 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12203 {
12204   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12205   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12206   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12207   int newx = x + dx;
12208   int newy = y + dy;
12209
12210   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12211           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12212           (IS_DIGGABLE(Feld[newx][newy]) ||
12213            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12214            canPassField(newx, newy, move_dir)));
12215 }
12216
12217 static void CheckGravityMovement(struct PlayerInfo *player)
12218 {
12219   if (player->gravity && !player->programmed_action)
12220   {
12221     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12222     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12223     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12224     int jx = player->jx, jy = player->jy;
12225     boolean player_is_moving_to_valid_field =
12226       (!player_is_snapping &&
12227        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12228         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12229     boolean player_can_fall_down = canFallDown(player);
12230
12231     if (player_can_fall_down &&
12232         !player_is_moving_to_valid_field)
12233       player->programmed_action = MV_DOWN;
12234   }
12235 }
12236
12237 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12238 {
12239   return CheckGravityMovement(player);
12240
12241   if (player->gravity && !player->programmed_action)
12242   {
12243     int jx = player->jx, jy = player->jy;
12244     boolean field_under_player_is_free =
12245       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12246     boolean player_is_standing_on_valid_field =
12247       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12248        (IS_WALKABLE(Feld[jx][jy]) &&
12249         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12250
12251     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12252       player->programmed_action = MV_DOWN;
12253   }
12254 }
12255
12256 /*
12257   MovePlayerOneStep()
12258   -----------------------------------------------------------------------------
12259   dx, dy:               direction (non-diagonal) to try to move the player to
12260   real_dx, real_dy:     direction as read from input device (can be diagonal)
12261 */
12262
12263 boolean MovePlayerOneStep(struct PlayerInfo *player,
12264                           int dx, int dy, int real_dx, int real_dy)
12265 {
12266   int jx = player->jx, jy = player->jy;
12267   int new_jx = jx + dx, new_jy = jy + dy;
12268   int can_move;
12269   boolean player_can_move = !player->cannot_move;
12270
12271   if (!player->active || (!dx && !dy))
12272     return MP_NO_ACTION;
12273
12274   player->MovDir = (dx < 0 ? MV_LEFT :
12275                     dx > 0 ? MV_RIGHT :
12276                     dy < 0 ? MV_UP :
12277                     dy > 0 ? MV_DOWN :  MV_NONE);
12278
12279   if (!IN_LEV_FIELD(new_jx, new_jy))
12280     return MP_NO_ACTION;
12281
12282   if (!player_can_move)
12283   {
12284     if (player->MovPos == 0)
12285     {
12286       player->is_moving = FALSE;
12287       player->is_digging = FALSE;
12288       player->is_collecting = FALSE;
12289       player->is_snapping = FALSE;
12290       player->is_pushing = FALSE;
12291     }
12292   }
12293
12294   if (!network.enabled && game.centered_player_nr == -1 &&
12295       !AllPlayersInSight(player, new_jx, new_jy))
12296     return MP_NO_ACTION;
12297
12298   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12299   if (can_move != MP_MOVING)
12300     return can_move;
12301
12302   /* check if DigField() has caused relocation of the player */
12303   if (player->jx != jx || player->jy != jy)
12304     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12305
12306   StorePlayer[jx][jy] = 0;
12307   player->last_jx = jx;
12308   player->last_jy = jy;
12309   player->jx = new_jx;
12310   player->jy = new_jy;
12311   StorePlayer[new_jx][new_jy] = player->element_nr;
12312
12313   if (player->move_delay_value_next != -1)
12314   {
12315     player->move_delay_value = player->move_delay_value_next;
12316     player->move_delay_value_next = -1;
12317   }
12318
12319   player->MovPos =
12320     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12321
12322   player->step_counter++;
12323
12324   PlayerVisit[jx][jy] = FrameCounter;
12325
12326   player->is_moving = TRUE;
12327
12328 #if 1
12329   /* should better be called in MovePlayer(), but this breaks some tapes */
12330   ScrollPlayer(player, SCROLL_INIT);
12331 #endif
12332
12333   return MP_MOVING;
12334 }
12335
12336 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12337 {
12338   int jx = player->jx, jy = player->jy;
12339   int old_jx = jx, old_jy = jy;
12340   int moved = MP_NO_ACTION;
12341
12342   if (!player->active)
12343     return FALSE;
12344
12345   if (!dx && !dy)
12346   {
12347     if (player->MovPos == 0)
12348     {
12349       player->is_moving = FALSE;
12350       player->is_digging = FALSE;
12351       player->is_collecting = FALSE;
12352       player->is_snapping = FALSE;
12353       player->is_pushing = FALSE;
12354     }
12355
12356     return FALSE;
12357   }
12358
12359   if (player->move_delay > 0)
12360     return FALSE;
12361
12362   player->move_delay = -1;              /* set to "uninitialized" value */
12363
12364   /* store if player is automatically moved to next field */
12365   player->is_auto_moving = (player->programmed_action != MV_NONE);
12366
12367   /* remove the last programmed player action */
12368   player->programmed_action = 0;
12369
12370   if (player->MovPos)
12371   {
12372     /* should only happen if pre-1.2 tape recordings are played */
12373     /* this is only for backward compatibility */
12374
12375     int original_move_delay_value = player->move_delay_value;
12376
12377 #if DEBUG
12378     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12379            tape.counter);
12380 #endif
12381
12382     /* scroll remaining steps with finest movement resolution */
12383     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12384
12385     while (player->MovPos)
12386     {
12387       ScrollPlayer(player, SCROLL_GO_ON);
12388       ScrollScreen(NULL, SCROLL_GO_ON);
12389
12390       AdvanceFrameAndPlayerCounters(player->index_nr);
12391
12392       DrawAllPlayers();
12393       BackToFront_WithFrameDelay(0);
12394     }
12395
12396     player->move_delay_value = original_move_delay_value;
12397   }
12398
12399   player->is_active = FALSE;
12400
12401   if (player->last_move_dir & MV_HORIZONTAL)
12402   {
12403     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12404       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12405   }
12406   else
12407   {
12408     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12409       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12410   }
12411
12412   if (!moved && !player->is_active)
12413   {
12414     player->is_moving = FALSE;
12415     player->is_digging = FALSE;
12416     player->is_collecting = FALSE;
12417     player->is_snapping = FALSE;
12418     player->is_pushing = FALSE;
12419   }
12420
12421   jx = player->jx;
12422   jy = player->jy;
12423
12424   if (moved & MP_MOVING && !ScreenMovPos &&
12425       (player->index_nr == game.centered_player_nr ||
12426        game.centered_player_nr == -1))
12427   {
12428     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12429     int offset = game.scroll_delay_value;
12430
12431     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12432     {
12433       /* actual player has left the screen -- scroll in that direction */
12434       if (jx != old_jx)         /* player has moved horizontally */
12435         scroll_x += (jx - old_jx);
12436       else                      /* player has moved vertically */
12437         scroll_y += (jy - old_jy);
12438     }
12439     else
12440     {
12441       if (jx != old_jx)         /* player has moved horizontally */
12442       {
12443         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12444             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12445           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12446
12447         /* don't scroll over playfield boundaries */
12448         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12449           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12450
12451         /* don't scroll more than one field at a time */
12452         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12453
12454         /* don't scroll against the player's moving direction */
12455         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12456             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12457           scroll_x = old_scroll_x;
12458       }
12459       else                      /* player has moved vertically */
12460       {
12461         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12462             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12463           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12464
12465         /* don't scroll over playfield boundaries */
12466         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12467           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12468
12469         /* don't scroll more than one field at a time */
12470         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12471
12472         /* don't scroll against the player's moving direction */
12473         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12474             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12475           scroll_y = old_scroll_y;
12476       }
12477     }
12478
12479     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12480     {
12481       if (!network.enabled && game.centered_player_nr == -1 &&
12482           !AllPlayersInVisibleScreen())
12483       {
12484         scroll_x = old_scroll_x;
12485         scroll_y = old_scroll_y;
12486       }
12487       else
12488       {
12489         ScrollScreen(player, SCROLL_INIT);
12490         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12491       }
12492     }
12493   }
12494
12495   player->StepFrame = 0;
12496
12497   if (moved & MP_MOVING)
12498   {
12499     if (old_jx != jx && old_jy == jy)
12500       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12501     else if (old_jx == jx && old_jy != jy)
12502       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12503
12504     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12505
12506     player->last_move_dir = player->MovDir;
12507     player->is_moving = TRUE;
12508     player->is_snapping = FALSE;
12509     player->is_switching = FALSE;
12510     player->is_dropping = FALSE;
12511     player->is_dropping_pressed = FALSE;
12512     player->drop_pressed_delay = 0;
12513
12514 #if 0
12515     /* should better be called here than above, but this breaks some tapes */
12516     ScrollPlayer(player, SCROLL_INIT);
12517 #endif
12518   }
12519   else
12520   {
12521     CheckGravityMovementWhenNotMoving(player);
12522
12523     player->is_moving = FALSE;
12524
12525     /* at this point, the player is allowed to move, but cannot move right now
12526        (e.g. because of something blocking the way) -- ensure that the player
12527        is also allowed to move in the next frame (in old versions before 3.1.1,
12528        the player was forced to wait again for eight frames before next try) */
12529
12530     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12531       player->move_delay = 0;   /* allow direct movement in the next frame */
12532   }
12533
12534   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12535     player->move_delay = player->move_delay_value;
12536
12537   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12538   {
12539     TestIfPlayerTouchesBadThing(jx, jy);
12540     TestIfPlayerTouchesCustomElement(jx, jy);
12541   }
12542
12543   if (!player->active)
12544     RemovePlayer(player);
12545
12546   return moved;
12547 }
12548
12549 void ScrollPlayer(struct PlayerInfo *player, int mode)
12550 {
12551   int jx = player->jx, jy = player->jy;
12552   int last_jx = player->last_jx, last_jy = player->last_jy;
12553   int move_stepsize = TILEX / player->move_delay_value;
12554
12555   if (!player->active)
12556     return;
12557
12558   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12559     return;
12560
12561   if (mode == SCROLL_INIT)
12562   {
12563     player->actual_frame_counter = FrameCounter;
12564     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12565
12566     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12567         Feld[last_jx][last_jy] == EL_EMPTY)
12568     {
12569       int last_field_block_delay = 0;   /* start with no blocking at all */
12570       int block_delay_adjustment = player->block_delay_adjustment;
12571
12572       /* if player blocks last field, add delay for exactly one move */
12573       if (player->block_last_field)
12574       {
12575         last_field_block_delay += player->move_delay_value;
12576
12577         /* when blocking enabled, prevent moving up despite gravity */
12578         if (player->gravity && player->MovDir == MV_UP)
12579           block_delay_adjustment = -1;
12580       }
12581
12582       /* add block delay adjustment (also possible when not blocking) */
12583       last_field_block_delay += block_delay_adjustment;
12584
12585       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12586       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12587     }
12588
12589     if (player->MovPos != 0)    /* player has not yet reached destination */
12590       return;
12591   }
12592   else if (!FrameReached(&player->actual_frame_counter, 1))
12593     return;
12594
12595   if (player->MovPos != 0)
12596   {
12597     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12598     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12599
12600     /* before DrawPlayer() to draw correct player graphic for this case */
12601     if (player->MovPos == 0)
12602       CheckGravityMovement(player);
12603   }
12604
12605   if (player->MovPos == 0)      /* player reached destination field */
12606   {
12607     if (player->move_delay_reset_counter > 0)
12608     {
12609       player->move_delay_reset_counter--;
12610
12611       if (player->move_delay_reset_counter == 0)
12612       {
12613         /* continue with normal speed after quickly moving through gate */
12614         HALVE_PLAYER_SPEED(player);
12615
12616         /* be able to make the next move without delay */
12617         player->move_delay = 0;
12618       }
12619     }
12620
12621     player->last_jx = jx;
12622     player->last_jy = jy;
12623
12624     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12625         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12626         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12627         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12628         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12629         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12630         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12631         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12632     {
12633       DrawPlayer(player);       /* needed here only to cleanup last field */
12634       RemovePlayer(player);
12635
12636       if (local_player->friends_still_needed == 0 ||
12637           IS_SP_ELEMENT(Feld[jx][jy]))
12638         PlayerWins(player);
12639     }
12640
12641     /* this breaks one level: "machine", level 000 */
12642     {
12643       int move_direction = player->MovDir;
12644       int enter_side = MV_DIR_OPPOSITE(move_direction);
12645       int leave_side = move_direction;
12646       int old_jx = last_jx;
12647       int old_jy = last_jy;
12648       int old_element = Feld[old_jx][old_jy];
12649       int new_element = Feld[jx][jy];
12650
12651       if (IS_CUSTOM_ELEMENT(old_element))
12652         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12653                                    CE_LEFT_BY_PLAYER,
12654                                    player->index_bit, leave_side);
12655
12656       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12657                                           CE_PLAYER_LEAVES_X,
12658                                           player->index_bit, leave_side);
12659
12660       if (IS_CUSTOM_ELEMENT(new_element))
12661         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12662                                    player->index_bit, enter_side);
12663
12664       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12665                                           CE_PLAYER_ENTERS_X,
12666                                           player->index_bit, enter_side);
12667
12668       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12669                                         CE_MOVE_OF_X, move_direction);
12670     }
12671
12672     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12673     {
12674       TestIfPlayerTouchesBadThing(jx, jy);
12675       TestIfPlayerTouchesCustomElement(jx, jy);
12676
12677       /* needed because pushed element has not yet reached its destination,
12678          so it would trigger a change event at its previous field location */
12679       if (!player->is_pushing)
12680         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12681
12682       if (!player->active)
12683         RemovePlayer(player);
12684     }
12685
12686     if (!local_player->LevelSolved && level.use_step_counter)
12687     {
12688       int i;
12689
12690       TimePlayed++;
12691
12692       if (TimeLeft > 0)
12693       {
12694         TimeLeft--;
12695
12696         if (TimeLeft <= 10 && setup.time_limit)
12697           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12698
12699         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12700
12701         DisplayGameControlValues();
12702
12703         if (!TimeLeft && setup.time_limit)
12704           for (i = 0; i < MAX_PLAYERS; i++)
12705             KillPlayer(&stored_player[i]);
12706       }
12707       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12708       {
12709         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12710
12711         DisplayGameControlValues();
12712       }
12713     }
12714
12715     if (tape.single_step && tape.recording && !tape.pausing &&
12716         !player->programmed_action)
12717       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12718
12719     if (!player->programmed_action)
12720       CheckSaveEngineSnapshot(player);
12721   }
12722 }
12723
12724 void ScrollScreen(struct PlayerInfo *player, int mode)
12725 {
12726   static unsigned int screen_frame_counter = 0;
12727
12728   if (mode == SCROLL_INIT)
12729   {
12730     /* set scrolling step size according to actual player's moving speed */
12731     ScrollStepSize = TILEX / player->move_delay_value;
12732
12733     screen_frame_counter = FrameCounter;
12734     ScreenMovDir = player->MovDir;
12735     ScreenMovPos = player->MovPos;
12736     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12737     return;
12738   }
12739   else if (!FrameReached(&screen_frame_counter, 1))
12740     return;
12741
12742   if (ScreenMovPos)
12743   {
12744     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12745     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12746     redraw_mask |= REDRAW_FIELD;
12747   }
12748   else
12749     ScreenMovDir = MV_NONE;
12750 }
12751
12752 void TestIfPlayerTouchesCustomElement(int x, int y)
12753 {
12754   static int xy[4][2] =
12755   {
12756     { 0, -1 },
12757     { -1, 0 },
12758     { +1, 0 },
12759     { 0, +1 }
12760   };
12761   static int trigger_sides[4][2] =
12762   {
12763     /* center side       border side */
12764     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12765     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12766     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12767     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12768   };
12769   static int touch_dir[4] =
12770   {
12771     MV_LEFT | MV_RIGHT,
12772     MV_UP   | MV_DOWN,
12773     MV_UP   | MV_DOWN,
12774     MV_LEFT | MV_RIGHT
12775   };
12776   int center_element = Feld[x][y];      /* should always be non-moving! */
12777   int i;
12778
12779   for (i = 0; i < NUM_DIRECTIONS; i++)
12780   {
12781     int xx = x + xy[i][0];
12782     int yy = y + xy[i][1];
12783     int center_side = trigger_sides[i][0];
12784     int border_side = trigger_sides[i][1];
12785     int border_element;
12786
12787     if (!IN_LEV_FIELD(xx, yy))
12788       continue;
12789
12790     if (IS_PLAYER(x, y))                /* player found at center element */
12791     {
12792       struct PlayerInfo *player = PLAYERINFO(x, y);
12793
12794       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12795         border_element = Feld[xx][yy];          /* may be moving! */
12796       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12797         border_element = Feld[xx][yy];
12798       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12799         border_element = MovingOrBlocked2Element(xx, yy);
12800       else
12801         continue;               /* center and border element do not touch */
12802
12803       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12804                                  player->index_bit, border_side);
12805       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12806                                           CE_PLAYER_TOUCHES_X,
12807                                           player->index_bit, border_side);
12808
12809       {
12810         /* use player element that is initially defined in the level playfield,
12811            not the player element that corresponds to the runtime player number
12812            (example: a level that contains EL_PLAYER_3 as the only player would
12813            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12814         int player_element = PLAYERINFO(x, y)->initial_element;
12815
12816         CheckElementChangeBySide(xx, yy, border_element, player_element,
12817                                  CE_TOUCHING_X, border_side);
12818       }
12819     }
12820     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12821     {
12822       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12823
12824       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12825       {
12826         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12827           continue;             /* center and border element do not touch */
12828       }
12829
12830       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12831                                  player->index_bit, center_side);
12832       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12833                                           CE_PLAYER_TOUCHES_X,
12834                                           player->index_bit, center_side);
12835
12836       {
12837         /* use player element that is initially defined in the level playfield,
12838            not the player element that corresponds to the runtime player number
12839            (example: a level that contains EL_PLAYER_3 as the only player would
12840            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12841         int player_element = PLAYERINFO(xx, yy)->initial_element;
12842
12843         CheckElementChangeBySide(x, y, center_element, player_element,
12844                                  CE_TOUCHING_X, center_side);
12845       }
12846
12847       break;
12848     }
12849   }
12850 }
12851
12852 void TestIfElementTouchesCustomElement(int x, int y)
12853 {
12854   static int xy[4][2] =
12855   {
12856     { 0, -1 },
12857     { -1, 0 },
12858     { +1, 0 },
12859     { 0, +1 }
12860   };
12861   static int trigger_sides[4][2] =
12862   {
12863     /* center side      border side */
12864     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12865     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12866     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12867     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12868   };
12869   static int touch_dir[4] =
12870   {
12871     MV_LEFT | MV_RIGHT,
12872     MV_UP   | MV_DOWN,
12873     MV_UP   | MV_DOWN,
12874     MV_LEFT | MV_RIGHT
12875   };
12876   boolean change_center_element = FALSE;
12877   int center_element = Feld[x][y];      /* should always be non-moving! */
12878   int border_element_old[NUM_DIRECTIONS];
12879   int i;
12880
12881   for (i = 0; i < NUM_DIRECTIONS; i++)
12882   {
12883     int xx = x + xy[i][0];
12884     int yy = y + xy[i][1];
12885     int border_element;
12886
12887     border_element_old[i] = -1;
12888
12889     if (!IN_LEV_FIELD(xx, yy))
12890       continue;
12891
12892     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12893       border_element = Feld[xx][yy];    /* may be moving! */
12894     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12895       border_element = Feld[xx][yy];
12896     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12897       border_element = MovingOrBlocked2Element(xx, yy);
12898     else
12899       continue;                 /* center and border element do not touch */
12900
12901     border_element_old[i] = border_element;
12902   }
12903
12904   for (i = 0; i < NUM_DIRECTIONS; i++)
12905   {
12906     int xx = x + xy[i][0];
12907     int yy = y + xy[i][1];
12908     int center_side = trigger_sides[i][0];
12909     int border_element = border_element_old[i];
12910
12911     if (border_element == -1)
12912       continue;
12913
12914     /* check for change of border element */
12915     CheckElementChangeBySide(xx, yy, border_element, center_element,
12916                              CE_TOUCHING_X, center_side);
12917
12918     /* (center element cannot be player, so we dont have to check this here) */
12919   }
12920
12921   for (i = 0; i < NUM_DIRECTIONS; i++)
12922   {
12923     int xx = x + xy[i][0];
12924     int yy = y + xy[i][1];
12925     int border_side = trigger_sides[i][1];
12926     int border_element = border_element_old[i];
12927
12928     if (border_element == -1)
12929       continue;
12930
12931     /* check for change of center element (but change it only once) */
12932     if (!change_center_element)
12933       change_center_element =
12934         CheckElementChangeBySide(x, y, center_element, border_element,
12935                                  CE_TOUCHING_X, border_side);
12936
12937     if (IS_PLAYER(xx, yy))
12938     {
12939       /* use player element that is initially defined in the level playfield,
12940          not the player element that corresponds to the runtime player number
12941          (example: a level that contains EL_PLAYER_3 as the only player would
12942          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12943       int player_element = PLAYERINFO(xx, yy)->initial_element;
12944
12945       CheckElementChangeBySide(x, y, center_element, player_element,
12946                                CE_TOUCHING_X, border_side);
12947     }
12948   }
12949 }
12950
12951 void TestIfElementHitsCustomElement(int x, int y, int direction)
12952 {
12953   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12954   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12955   int hitx = x + dx, hity = y + dy;
12956   int hitting_element = Feld[x][y];
12957   int touched_element;
12958
12959   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12960     return;
12961
12962   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12963                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12964
12965   if (IN_LEV_FIELD(hitx, hity))
12966   {
12967     int opposite_direction = MV_DIR_OPPOSITE(direction);
12968     int hitting_side = direction;
12969     int touched_side = opposite_direction;
12970     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12971                           MovDir[hitx][hity] != direction ||
12972                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12973
12974     object_hit = TRUE;
12975
12976     if (object_hit)
12977     {
12978       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12979                                CE_HITTING_X, touched_side);
12980
12981       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12982                                CE_HIT_BY_X, hitting_side);
12983
12984       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12985                                CE_HIT_BY_SOMETHING, opposite_direction);
12986
12987       if (IS_PLAYER(hitx, hity))
12988       {
12989         /* use player element that is initially defined in the level playfield,
12990            not the player element that corresponds to the runtime player number
12991            (example: a level that contains EL_PLAYER_3 as the only player would
12992            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12993         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12994
12995         CheckElementChangeBySide(x, y, hitting_element, player_element,
12996                                  CE_HITTING_X, touched_side);
12997       }
12998     }
12999   }
13000
13001   /* "hitting something" is also true when hitting the playfield border */
13002   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13003                            CE_HITTING_SOMETHING, direction);
13004 }
13005
13006 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13007 {
13008   int i, kill_x = -1, kill_y = -1;
13009
13010   int bad_element = -1;
13011   static int test_xy[4][2] =
13012   {
13013     { 0, -1 },
13014     { -1, 0 },
13015     { +1, 0 },
13016     { 0, +1 }
13017   };
13018   static int test_dir[4] =
13019   {
13020     MV_UP,
13021     MV_LEFT,
13022     MV_RIGHT,
13023     MV_DOWN
13024   };
13025
13026   for (i = 0; i < NUM_DIRECTIONS; i++)
13027   {
13028     int test_x, test_y, test_move_dir, test_element;
13029
13030     test_x = good_x + test_xy[i][0];
13031     test_y = good_y + test_xy[i][1];
13032
13033     if (!IN_LEV_FIELD(test_x, test_y))
13034       continue;
13035
13036     test_move_dir =
13037       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13038
13039     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13040
13041     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13042        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13043     */
13044     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13045         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13046     {
13047       kill_x = test_x;
13048       kill_y = test_y;
13049       bad_element = test_element;
13050
13051       break;
13052     }
13053   }
13054
13055   if (kill_x != -1 || kill_y != -1)
13056   {
13057     if (IS_PLAYER(good_x, good_y))
13058     {
13059       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13060
13061       if (player->shield_deadly_time_left > 0 &&
13062           !IS_INDESTRUCTIBLE(bad_element))
13063         Bang(kill_x, kill_y);
13064       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13065         KillPlayer(player);
13066     }
13067     else
13068       Bang(good_x, good_y);
13069   }
13070 }
13071
13072 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13073 {
13074   int i, kill_x = -1, kill_y = -1;
13075   int bad_element = Feld[bad_x][bad_y];
13076   static int test_xy[4][2] =
13077   {
13078     { 0, -1 },
13079     { -1, 0 },
13080     { +1, 0 },
13081     { 0, +1 }
13082   };
13083   static int touch_dir[4] =
13084   {
13085     MV_LEFT | MV_RIGHT,
13086     MV_UP   | MV_DOWN,
13087     MV_UP   | MV_DOWN,
13088     MV_LEFT | MV_RIGHT
13089   };
13090   static int test_dir[4] =
13091   {
13092     MV_UP,
13093     MV_LEFT,
13094     MV_RIGHT,
13095     MV_DOWN
13096   };
13097
13098   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13099     return;
13100
13101   for (i = 0; i < NUM_DIRECTIONS; i++)
13102   {
13103     int test_x, test_y, test_move_dir, test_element;
13104
13105     test_x = bad_x + test_xy[i][0];
13106     test_y = bad_y + test_xy[i][1];
13107
13108     if (!IN_LEV_FIELD(test_x, test_y))
13109       continue;
13110
13111     test_move_dir =
13112       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13113
13114     test_element = Feld[test_x][test_y];
13115
13116     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13117        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13118     */
13119     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13120         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13121     {
13122       /* good thing is player or penguin that does not move away */
13123       if (IS_PLAYER(test_x, test_y))
13124       {
13125         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13126
13127         if (bad_element == EL_ROBOT && player->is_moving)
13128           continue;     /* robot does not kill player if he is moving */
13129
13130         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13131         {
13132           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13133             continue;           /* center and border element do not touch */
13134         }
13135
13136         kill_x = test_x;
13137         kill_y = test_y;
13138
13139         break;
13140       }
13141       else if (test_element == EL_PENGUIN)
13142       {
13143         kill_x = test_x;
13144         kill_y = test_y;
13145
13146         break;
13147       }
13148     }
13149   }
13150
13151   if (kill_x != -1 || kill_y != -1)
13152   {
13153     if (IS_PLAYER(kill_x, kill_y))
13154     {
13155       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13156
13157       if (player->shield_deadly_time_left > 0 &&
13158           !IS_INDESTRUCTIBLE(bad_element))
13159         Bang(bad_x, bad_y);
13160       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13161         KillPlayer(player);
13162     }
13163     else
13164       Bang(kill_x, kill_y);
13165   }
13166 }
13167
13168 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13169 {
13170   int bad_element = Feld[bad_x][bad_y];
13171   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13172   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13173   int test_x = bad_x + dx, test_y = bad_y + dy;
13174   int test_move_dir, test_element;
13175   int kill_x = -1, kill_y = -1;
13176
13177   if (!IN_LEV_FIELD(test_x, test_y))
13178     return;
13179
13180   test_move_dir =
13181     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13182
13183   test_element = Feld[test_x][test_y];
13184
13185   if (test_move_dir != bad_move_dir)
13186   {
13187     /* good thing can be player or penguin that does not move away */
13188     if (IS_PLAYER(test_x, test_y))
13189     {
13190       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13191
13192       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13193          player as being hit when he is moving towards the bad thing, because
13194          the "get hit by" condition would be lost after the player stops) */
13195       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13196         return;         /* player moves away from bad thing */
13197
13198       kill_x = test_x;
13199       kill_y = test_y;
13200     }
13201     else if (test_element == EL_PENGUIN)
13202     {
13203       kill_x = test_x;
13204       kill_y = test_y;
13205     }
13206   }
13207
13208   if (kill_x != -1 || kill_y != -1)
13209   {
13210     if (IS_PLAYER(kill_x, kill_y))
13211     {
13212       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13213
13214       if (player->shield_deadly_time_left > 0 &&
13215           !IS_INDESTRUCTIBLE(bad_element))
13216         Bang(bad_x, bad_y);
13217       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13218         KillPlayer(player);
13219     }
13220     else
13221       Bang(kill_x, kill_y);
13222   }
13223 }
13224
13225 void TestIfPlayerTouchesBadThing(int x, int y)
13226 {
13227   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13228 }
13229
13230 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13231 {
13232   TestIfGoodThingHitsBadThing(x, y, move_dir);
13233 }
13234
13235 void TestIfBadThingTouchesPlayer(int x, int y)
13236 {
13237   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13238 }
13239
13240 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13241 {
13242   TestIfBadThingHitsGoodThing(x, y, move_dir);
13243 }
13244
13245 void TestIfFriendTouchesBadThing(int x, int y)
13246 {
13247   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13248 }
13249
13250 void TestIfBadThingTouchesFriend(int x, int y)
13251 {
13252   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13253 }
13254
13255 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13256 {
13257   int i, kill_x = bad_x, kill_y = bad_y;
13258   static int xy[4][2] =
13259   {
13260     { 0, -1 },
13261     { -1, 0 },
13262     { +1, 0 },
13263     { 0, +1 }
13264   };
13265
13266   for (i = 0; i < NUM_DIRECTIONS; i++)
13267   {
13268     int x, y, element;
13269
13270     x = bad_x + xy[i][0];
13271     y = bad_y + xy[i][1];
13272     if (!IN_LEV_FIELD(x, y))
13273       continue;
13274
13275     element = Feld[x][y];
13276     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13277         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13278     {
13279       kill_x = x;
13280       kill_y = y;
13281       break;
13282     }
13283   }
13284
13285   if (kill_x != bad_x || kill_y != bad_y)
13286     Bang(bad_x, bad_y);
13287 }
13288
13289 void KillPlayer(struct PlayerInfo *player)
13290 {
13291   int jx = player->jx, jy = player->jy;
13292
13293   if (!player->active)
13294     return;
13295
13296 #if 0
13297   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13298          player->killed, player->active, player->reanimated);
13299 #endif
13300
13301   /* the following code was introduced to prevent an infinite loop when calling
13302      -> Bang()
13303      -> CheckTriggeredElementChangeExt()
13304      -> ExecuteCustomElementAction()
13305      -> KillPlayer()
13306      -> (infinitely repeating the above sequence of function calls)
13307      which occurs when killing the player while having a CE with the setting
13308      "kill player X when explosion of <player X>"; the solution using a new
13309      field "player->killed" was chosen for backwards compatibility, although
13310      clever use of the fields "player->active" etc. would probably also work */
13311 #if 1
13312   if (player->killed)
13313     return;
13314 #endif
13315
13316   player->killed = TRUE;
13317
13318   /* remove accessible field at the player's position */
13319   Feld[jx][jy] = EL_EMPTY;
13320
13321   /* deactivate shield (else Bang()/Explode() would not work right) */
13322   player->shield_normal_time_left = 0;
13323   player->shield_deadly_time_left = 0;
13324
13325 #if 0
13326   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13327          player->killed, player->active, player->reanimated);
13328 #endif
13329
13330   Bang(jx, jy);
13331
13332 #if 0
13333   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13334          player->killed, player->active, player->reanimated);
13335 #endif
13336
13337   if (player->reanimated)       /* killed player may have been reanimated */
13338     player->killed = player->reanimated = FALSE;
13339   else
13340     BuryPlayer(player);
13341 }
13342
13343 static void KillPlayerUnlessEnemyProtected(int x, int y)
13344 {
13345   if (!PLAYER_ENEMY_PROTECTED(x, y))
13346     KillPlayer(PLAYERINFO(x, y));
13347 }
13348
13349 static void KillPlayerUnlessExplosionProtected(int x, int y)
13350 {
13351   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13352     KillPlayer(PLAYERINFO(x, y));
13353 }
13354
13355 void BuryPlayer(struct PlayerInfo *player)
13356 {
13357   int jx = player->jx, jy = player->jy;
13358
13359   if (!player->active)
13360     return;
13361
13362   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13363   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13364
13365   player->GameOver = TRUE;
13366   RemovePlayer(player);
13367 }
13368
13369 void RemovePlayer(struct PlayerInfo *player)
13370 {
13371   int jx = player->jx, jy = player->jy;
13372   int i, found = FALSE;
13373
13374   player->present = FALSE;
13375   player->active = FALSE;
13376
13377   if (!ExplodeField[jx][jy])
13378     StorePlayer[jx][jy] = 0;
13379
13380   if (player->is_moving)
13381     TEST_DrawLevelField(player->last_jx, player->last_jy);
13382
13383   for (i = 0; i < MAX_PLAYERS; i++)
13384     if (stored_player[i].active)
13385       found = TRUE;
13386
13387   if (!found)
13388     AllPlayersGone = TRUE;
13389
13390   ExitX = ZX = jx;
13391   ExitY = ZY = jy;
13392 }
13393
13394 static void setFieldForSnapping(int x, int y, int element, int direction)
13395 {
13396   struct ElementInfo *ei = &element_info[element];
13397   int direction_bit = MV_DIR_TO_BIT(direction);
13398   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13399   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13400                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13401
13402   Feld[x][y] = EL_ELEMENT_SNAPPING;
13403   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13404
13405   ResetGfxAnimation(x, y);
13406
13407   GfxElement[x][y] = element;
13408   GfxAction[x][y] = action;
13409   GfxDir[x][y] = direction;
13410   GfxFrame[x][y] = -1;
13411 }
13412
13413 /*
13414   =============================================================================
13415   checkDiagonalPushing()
13416   -----------------------------------------------------------------------------
13417   check if diagonal input device direction results in pushing of object
13418   (by checking if the alternative direction is walkable, diggable, ...)
13419   =============================================================================
13420 */
13421
13422 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13423                                     int x, int y, int real_dx, int real_dy)
13424 {
13425   int jx, jy, dx, dy, xx, yy;
13426
13427   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13428     return TRUE;
13429
13430   /* diagonal direction: check alternative direction */
13431   jx = player->jx;
13432   jy = player->jy;
13433   dx = x - jx;
13434   dy = y - jy;
13435   xx = jx + (dx == 0 ? real_dx : 0);
13436   yy = jy + (dy == 0 ? real_dy : 0);
13437
13438   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13439 }
13440
13441 /*
13442   =============================================================================
13443   DigField()
13444   -----------------------------------------------------------------------------
13445   x, y:                 field next to player (non-diagonal) to try to dig to
13446   real_dx, real_dy:     direction as read from input device (can be diagonal)
13447   =============================================================================
13448 */
13449
13450 static int DigField(struct PlayerInfo *player,
13451                     int oldx, int oldy, int x, int y,
13452                     int real_dx, int real_dy, int mode)
13453 {
13454   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13455   boolean player_was_pushing = player->is_pushing;
13456   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13457   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13458   int jx = oldx, jy = oldy;
13459   int dx = x - jx, dy = y - jy;
13460   int nextx = x + dx, nexty = y + dy;
13461   int move_direction = (dx == -1 ? MV_LEFT  :
13462                         dx == +1 ? MV_RIGHT :
13463                         dy == -1 ? MV_UP    :
13464                         dy == +1 ? MV_DOWN  : MV_NONE);
13465   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13466   int dig_side = MV_DIR_OPPOSITE(move_direction);
13467   int old_element = Feld[jx][jy];
13468   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13469   int collect_count;
13470
13471   if (is_player)                /* function can also be called by EL_PENGUIN */
13472   {
13473     if (player->MovPos == 0)
13474     {
13475       player->is_digging = FALSE;
13476       player->is_collecting = FALSE;
13477     }
13478
13479     if (player->MovPos == 0)    /* last pushing move finished */
13480       player->is_pushing = FALSE;
13481
13482     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13483     {
13484       player->is_switching = FALSE;
13485       player->push_delay = -1;
13486
13487       return MP_NO_ACTION;
13488     }
13489   }
13490
13491   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13492     old_element = Back[jx][jy];
13493
13494   /* in case of element dropped at player position, check background */
13495   else if (Back[jx][jy] != EL_EMPTY &&
13496            game.engine_version >= VERSION_IDENT(2,2,0,0))
13497     old_element = Back[jx][jy];
13498
13499   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13500     return MP_NO_ACTION;        /* field has no opening in this direction */
13501
13502   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13503     return MP_NO_ACTION;        /* field has no opening in this direction */
13504
13505   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13506   {
13507     SplashAcid(x, y);
13508
13509     Feld[jx][jy] = player->artwork_element;
13510     InitMovingField(jx, jy, MV_DOWN);
13511     Store[jx][jy] = EL_ACID;
13512     ContinueMoving(jx, jy);
13513     BuryPlayer(player);
13514
13515     return MP_DONT_RUN_INTO;
13516   }
13517
13518   if (player_can_move && DONT_RUN_INTO(element))
13519   {
13520     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13521
13522     return MP_DONT_RUN_INTO;
13523   }
13524
13525   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13526     return MP_NO_ACTION;
13527
13528   collect_count = element_info[element].collect_count_initial;
13529
13530   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13531     return MP_NO_ACTION;
13532
13533   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13534     player_can_move = player_can_move_or_snap;
13535
13536   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13537       game.engine_version >= VERSION_IDENT(2,2,0,0))
13538   {
13539     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13540                                player->index_bit, dig_side);
13541     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13542                                         player->index_bit, dig_side);
13543
13544     if (element == EL_DC_LANDMINE)
13545       Bang(x, y);
13546
13547     if (Feld[x][y] != element)          /* field changed by snapping */
13548       return MP_ACTION;
13549
13550     return MP_NO_ACTION;
13551   }
13552
13553   if (player->gravity && is_player && !player->is_auto_moving &&
13554       canFallDown(player) && move_direction != MV_DOWN &&
13555       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13556     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13557
13558   if (player_can_move &&
13559       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13560   {
13561     int sound_element = SND_ELEMENT(element);
13562     int sound_action = ACTION_WALKING;
13563
13564     if (IS_RND_GATE(element))
13565     {
13566       if (!player->key[RND_GATE_NR(element)])
13567         return MP_NO_ACTION;
13568     }
13569     else if (IS_RND_GATE_GRAY(element))
13570     {
13571       if (!player->key[RND_GATE_GRAY_NR(element)])
13572         return MP_NO_ACTION;
13573     }
13574     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13575     {
13576       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13577         return MP_NO_ACTION;
13578     }
13579     else if (element == EL_EXIT_OPEN ||
13580              element == EL_EM_EXIT_OPEN ||
13581              element == EL_EM_EXIT_OPENING ||
13582              element == EL_STEEL_EXIT_OPEN ||
13583              element == EL_EM_STEEL_EXIT_OPEN ||
13584              element == EL_EM_STEEL_EXIT_OPENING ||
13585              element == EL_SP_EXIT_OPEN ||
13586              element == EL_SP_EXIT_OPENING)
13587     {
13588       sound_action = ACTION_PASSING;    /* player is passing exit */
13589     }
13590     else if (element == EL_EMPTY)
13591     {
13592       sound_action = ACTION_MOVING;             /* nothing to walk on */
13593     }
13594
13595     /* play sound from background or player, whatever is available */
13596     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13597       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13598     else
13599       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13600   }
13601   else if (player_can_move &&
13602            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13603   {
13604     if (!ACCESS_FROM(element, opposite_direction))
13605       return MP_NO_ACTION;      /* field not accessible from this direction */
13606
13607     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13608       return MP_NO_ACTION;
13609
13610     if (IS_EM_GATE(element))
13611     {
13612       if (!player->key[EM_GATE_NR(element)])
13613         return MP_NO_ACTION;
13614     }
13615     else if (IS_EM_GATE_GRAY(element))
13616     {
13617       if (!player->key[EM_GATE_GRAY_NR(element)])
13618         return MP_NO_ACTION;
13619     }
13620     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13621     {
13622       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13623         return MP_NO_ACTION;
13624     }
13625     else if (IS_EMC_GATE(element))
13626     {
13627       if (!player->key[EMC_GATE_NR(element)])
13628         return MP_NO_ACTION;
13629     }
13630     else if (IS_EMC_GATE_GRAY(element))
13631     {
13632       if (!player->key[EMC_GATE_GRAY_NR(element)])
13633         return MP_NO_ACTION;
13634     }
13635     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13636     {
13637       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13638         return MP_NO_ACTION;
13639     }
13640     else if (element == EL_DC_GATE_WHITE ||
13641              element == EL_DC_GATE_WHITE_GRAY ||
13642              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13643     {
13644       if (player->num_white_keys == 0)
13645         return MP_NO_ACTION;
13646
13647       player->num_white_keys--;
13648     }
13649     else if (IS_SP_PORT(element))
13650     {
13651       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13652           element == EL_SP_GRAVITY_PORT_RIGHT ||
13653           element == EL_SP_GRAVITY_PORT_UP ||
13654           element == EL_SP_GRAVITY_PORT_DOWN)
13655         player->gravity = !player->gravity;
13656       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13657                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13658                element == EL_SP_GRAVITY_ON_PORT_UP ||
13659                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13660         player->gravity = TRUE;
13661       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13662                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13663                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13664                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13665         player->gravity = FALSE;
13666     }
13667
13668     /* automatically move to the next field with double speed */
13669     player->programmed_action = move_direction;
13670
13671     if (player->move_delay_reset_counter == 0)
13672     {
13673       player->move_delay_reset_counter = 2;     /* two double speed steps */
13674
13675       DOUBLE_PLAYER_SPEED(player);
13676     }
13677
13678     PlayLevelSoundAction(x, y, ACTION_PASSING);
13679   }
13680   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13681   {
13682     RemoveField(x, y);
13683
13684     if (mode != DF_SNAP)
13685     {
13686       GfxElement[x][y] = GFX_ELEMENT(element);
13687       player->is_digging = TRUE;
13688     }
13689
13690     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13691
13692     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13693                                         player->index_bit, dig_side);
13694
13695     if (mode == DF_SNAP)
13696     {
13697       if (level.block_snap_field)
13698         setFieldForSnapping(x, y, element, move_direction);
13699       else
13700         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13701
13702       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13703                                           player->index_bit, dig_side);
13704     }
13705   }
13706   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13707   {
13708     RemoveField(x, y);
13709
13710     if (is_player && mode != DF_SNAP)
13711     {
13712       GfxElement[x][y] = element;
13713       player->is_collecting = TRUE;
13714     }
13715
13716     if (element == EL_SPEED_PILL)
13717     {
13718       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13719     }
13720     else if (element == EL_EXTRA_TIME && level.time > 0)
13721     {
13722       TimeLeft += level.extra_time;
13723
13724       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13725
13726       DisplayGameControlValues();
13727     }
13728     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13729     {
13730       player->shield_normal_time_left += level.shield_normal_time;
13731       if (element == EL_SHIELD_DEADLY)
13732         player->shield_deadly_time_left += level.shield_deadly_time;
13733     }
13734     else if (element == EL_DYNAMITE ||
13735              element == EL_EM_DYNAMITE ||
13736              element == EL_SP_DISK_RED)
13737     {
13738       if (player->inventory_size < MAX_INVENTORY_SIZE)
13739         player->inventory_element[player->inventory_size++] = element;
13740
13741       DrawGameDoorValues();
13742     }
13743     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13744     {
13745       player->dynabomb_count++;
13746       player->dynabombs_left++;
13747     }
13748     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13749     {
13750       player->dynabomb_size++;
13751     }
13752     else if (element == EL_DYNABOMB_INCREASE_POWER)
13753     {
13754       player->dynabomb_xl = TRUE;
13755     }
13756     else if (IS_KEY(element))
13757     {
13758       player->key[KEY_NR(element)] = TRUE;
13759
13760       DrawGameDoorValues();
13761     }
13762     else if (element == EL_DC_KEY_WHITE)
13763     {
13764       player->num_white_keys++;
13765
13766       /* display white keys? */
13767       /* DrawGameDoorValues(); */
13768     }
13769     else if (IS_ENVELOPE(element))
13770     {
13771       player->show_envelope = element;
13772     }
13773     else if (element == EL_EMC_LENSES)
13774     {
13775       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13776
13777       RedrawAllInvisibleElementsForLenses();
13778     }
13779     else if (element == EL_EMC_MAGNIFIER)
13780     {
13781       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13782
13783       RedrawAllInvisibleElementsForMagnifier();
13784     }
13785     else if (IS_DROPPABLE(element) ||
13786              IS_THROWABLE(element))     /* can be collected and dropped */
13787     {
13788       int i;
13789
13790       if (collect_count == 0)
13791         player->inventory_infinite_element = element;
13792       else
13793         for (i = 0; i < collect_count; i++)
13794           if (player->inventory_size < MAX_INVENTORY_SIZE)
13795             player->inventory_element[player->inventory_size++] = element;
13796
13797       DrawGameDoorValues();
13798     }
13799     else if (collect_count > 0)
13800     {
13801       local_player->gems_still_needed -= collect_count;
13802       if (local_player->gems_still_needed < 0)
13803         local_player->gems_still_needed = 0;
13804
13805       game.snapshot.collected_item = TRUE;
13806
13807       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13808
13809       DisplayGameControlValues();
13810     }
13811
13812     RaiseScoreElement(element);
13813     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13814
13815     if (is_player)
13816       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13817                                           player->index_bit, dig_side);
13818
13819     if (mode == DF_SNAP)
13820     {
13821       if (level.block_snap_field)
13822         setFieldForSnapping(x, y, element, move_direction);
13823       else
13824         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13825
13826       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13827                                           player->index_bit, dig_side);
13828     }
13829   }
13830   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13831   {
13832     if (mode == DF_SNAP && element != EL_BD_ROCK)
13833       return MP_NO_ACTION;
13834
13835     if (CAN_FALL(element) && dy)
13836       return MP_NO_ACTION;
13837
13838     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13839         !(element == EL_SPRING && level.use_spring_bug))
13840       return MP_NO_ACTION;
13841
13842     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13843         ((move_direction & MV_VERTICAL &&
13844           ((element_info[element].move_pattern & MV_LEFT &&
13845             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13846            (element_info[element].move_pattern & MV_RIGHT &&
13847             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13848          (move_direction & MV_HORIZONTAL &&
13849           ((element_info[element].move_pattern & MV_UP &&
13850             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13851            (element_info[element].move_pattern & MV_DOWN &&
13852             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13853       return MP_NO_ACTION;
13854
13855     /* do not push elements already moving away faster than player */
13856     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13857         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13858       return MP_NO_ACTION;
13859
13860     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13861     {
13862       if (player->push_delay_value == -1 || !player_was_pushing)
13863         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13864     }
13865     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13866     {
13867       if (player->push_delay_value == -1)
13868         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13869     }
13870     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13871     {
13872       if (!player->is_pushing)
13873         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13874     }
13875
13876     player->is_pushing = TRUE;
13877     player->is_active = TRUE;
13878
13879     if (!(IN_LEV_FIELD(nextx, nexty) &&
13880           (IS_FREE(nextx, nexty) ||
13881            (IS_SB_ELEMENT(element) &&
13882             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13883            (IS_CUSTOM_ELEMENT(element) &&
13884             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13885       return MP_NO_ACTION;
13886
13887     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13888       return MP_NO_ACTION;
13889
13890     if (player->push_delay == -1)       /* new pushing; restart delay */
13891       player->push_delay = 0;
13892
13893     if (player->push_delay < player->push_delay_value &&
13894         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13895         element != EL_SPRING && element != EL_BALLOON)
13896     {
13897       /* make sure that there is no move delay before next try to push */
13898       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13899         player->move_delay = 0;
13900
13901       return MP_NO_ACTION;
13902     }
13903
13904     if (IS_CUSTOM_ELEMENT(element) &&
13905         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13906     {
13907       if (!DigFieldByCE(nextx, nexty, element))
13908         return MP_NO_ACTION;
13909     }
13910
13911     if (IS_SB_ELEMENT(element))
13912     {
13913       if (element == EL_SOKOBAN_FIELD_FULL)
13914       {
13915         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13916         local_player->sokobanfields_still_needed++;
13917       }
13918
13919       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13920       {
13921         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13922         local_player->sokobanfields_still_needed--;
13923       }
13924
13925       Feld[x][y] = EL_SOKOBAN_OBJECT;
13926
13927       if (Back[x][y] == Back[nextx][nexty])
13928         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13929       else if (Back[x][y] != 0)
13930         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13931                                     ACTION_EMPTYING);
13932       else
13933         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13934                                     ACTION_FILLING);
13935
13936       if (local_player->sokobanfields_still_needed == 0 &&
13937           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13938       {
13939         PlayerWins(player);
13940
13941         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13942       }
13943     }
13944     else
13945       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13946
13947     InitMovingField(x, y, move_direction);
13948     GfxAction[x][y] = ACTION_PUSHING;
13949
13950     if (mode == DF_SNAP)
13951       ContinueMoving(x, y);
13952     else
13953       MovPos[x][y] = (dx != 0 ? dx : dy);
13954
13955     Pushed[x][y] = TRUE;
13956     Pushed[nextx][nexty] = TRUE;
13957
13958     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13959       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13960     else
13961       player->push_delay_value = -1;    /* get new value later */
13962
13963     /* check for element change _after_ element has been pushed */
13964     if (game.use_change_when_pushing_bug)
13965     {
13966       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13967                                  player->index_bit, dig_side);
13968       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13969                                           player->index_bit, dig_side);
13970     }
13971   }
13972   else if (IS_SWITCHABLE(element))
13973   {
13974     if (PLAYER_SWITCHING(player, x, y))
13975     {
13976       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13977                                           player->index_bit, dig_side);
13978
13979       return MP_ACTION;
13980     }
13981
13982     player->is_switching = TRUE;
13983     player->switch_x = x;
13984     player->switch_y = y;
13985
13986     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13987
13988     if (element == EL_ROBOT_WHEEL)
13989     {
13990       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13991       ZX = x;
13992       ZY = y;
13993
13994       game.robot_wheel_active = TRUE;
13995
13996       TEST_DrawLevelField(x, y);
13997     }
13998     else if (element == EL_SP_TERMINAL)
13999     {
14000       int xx, yy;
14001
14002       SCAN_PLAYFIELD(xx, yy)
14003       {
14004         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14005         {
14006           Bang(xx, yy);
14007         }
14008         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14009         {
14010           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14011
14012           ResetGfxAnimation(xx, yy);
14013           TEST_DrawLevelField(xx, yy);
14014         }
14015       }
14016     }
14017     else if (IS_BELT_SWITCH(element))
14018     {
14019       ToggleBeltSwitch(x, y);
14020     }
14021     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14022              element == EL_SWITCHGATE_SWITCH_DOWN ||
14023              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14024              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14025     {
14026       ToggleSwitchgateSwitch(x, y);
14027     }
14028     else if (element == EL_LIGHT_SWITCH ||
14029              element == EL_LIGHT_SWITCH_ACTIVE)
14030     {
14031       ToggleLightSwitch(x, y);
14032     }
14033     else if (element == EL_TIMEGATE_SWITCH ||
14034              element == EL_DC_TIMEGATE_SWITCH)
14035     {
14036       ActivateTimegateSwitch(x, y);
14037     }
14038     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14039              element == EL_BALLOON_SWITCH_RIGHT ||
14040              element == EL_BALLOON_SWITCH_UP    ||
14041              element == EL_BALLOON_SWITCH_DOWN  ||
14042              element == EL_BALLOON_SWITCH_NONE  ||
14043              element == EL_BALLOON_SWITCH_ANY)
14044     {
14045       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14046                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14047                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14048                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14049                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14050                              move_direction);
14051     }
14052     else if (element == EL_LAMP)
14053     {
14054       Feld[x][y] = EL_LAMP_ACTIVE;
14055       local_player->lights_still_needed--;
14056
14057       ResetGfxAnimation(x, y);
14058       TEST_DrawLevelField(x, y);
14059     }
14060     else if (element == EL_TIME_ORB_FULL)
14061     {
14062       Feld[x][y] = EL_TIME_ORB_EMPTY;
14063
14064       if (level.time > 0 || level.use_time_orb_bug)
14065       {
14066         TimeLeft += level.time_orb_time;
14067         game.no_time_limit = FALSE;
14068
14069         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14070
14071         DisplayGameControlValues();
14072       }
14073
14074       ResetGfxAnimation(x, y);
14075       TEST_DrawLevelField(x, y);
14076     }
14077     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14078              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14079     {
14080       int xx, yy;
14081
14082       game.ball_state = !game.ball_state;
14083
14084       SCAN_PLAYFIELD(xx, yy)
14085       {
14086         int e = Feld[xx][yy];
14087
14088         if (game.ball_state)
14089         {
14090           if (e == EL_EMC_MAGIC_BALL)
14091             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14092           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14093             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14094         }
14095         else
14096         {
14097           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14098             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14099           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14100             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14101         }
14102       }
14103     }
14104
14105     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14106                                         player->index_bit, dig_side);
14107
14108     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14109                                         player->index_bit, dig_side);
14110
14111     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14112                                         player->index_bit, dig_side);
14113
14114     return MP_ACTION;
14115   }
14116   else
14117   {
14118     if (!PLAYER_SWITCHING(player, x, y))
14119     {
14120       player->is_switching = TRUE;
14121       player->switch_x = x;
14122       player->switch_y = y;
14123
14124       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14125                                  player->index_bit, dig_side);
14126       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14127                                           player->index_bit, dig_side);
14128
14129       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14130                                  player->index_bit, dig_side);
14131       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14132                                           player->index_bit, dig_side);
14133     }
14134
14135     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14136                                player->index_bit, dig_side);
14137     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14138                                         player->index_bit, dig_side);
14139
14140     return MP_NO_ACTION;
14141   }
14142
14143   player->push_delay = -1;
14144
14145   if (is_player)                /* function can also be called by EL_PENGUIN */
14146   {
14147     if (Feld[x][y] != element)          /* really digged/collected something */
14148     {
14149       player->is_collecting = !player->is_digging;
14150       player->is_active = TRUE;
14151     }
14152   }
14153
14154   return MP_MOVING;
14155 }
14156
14157 static boolean DigFieldByCE(int x, int y, int digging_element)
14158 {
14159   int element = Feld[x][y];
14160
14161   if (!IS_FREE(x, y))
14162   {
14163     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14164                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14165                   ACTION_BREAKING);
14166
14167     /* no element can dig solid indestructible elements */
14168     if (IS_INDESTRUCTIBLE(element) &&
14169         !IS_DIGGABLE(element) &&
14170         !IS_COLLECTIBLE(element))
14171       return FALSE;
14172
14173     if (AmoebaNr[x][y] &&
14174         (element == EL_AMOEBA_FULL ||
14175          element == EL_BD_AMOEBA ||
14176          element == EL_AMOEBA_GROWING))
14177     {
14178       AmoebaCnt[AmoebaNr[x][y]]--;
14179       AmoebaCnt2[AmoebaNr[x][y]]--;
14180     }
14181
14182     if (IS_MOVING(x, y))
14183       RemoveMovingField(x, y);
14184     else
14185     {
14186       RemoveField(x, y);
14187       TEST_DrawLevelField(x, y);
14188     }
14189
14190     /* if digged element was about to explode, prevent the explosion */
14191     ExplodeField[x][y] = EX_TYPE_NONE;
14192
14193     PlayLevelSoundAction(x, y, action);
14194   }
14195
14196   Store[x][y] = EL_EMPTY;
14197
14198   /* this makes it possible to leave the removed element again */
14199   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14200     Store[x][y] = element;
14201
14202   return TRUE;
14203 }
14204
14205 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14206 {
14207   int jx = player->jx, jy = player->jy;
14208   int x = jx + dx, y = jy + dy;
14209   int snap_direction = (dx == -1 ? MV_LEFT  :
14210                         dx == +1 ? MV_RIGHT :
14211                         dy == -1 ? MV_UP    :
14212                         dy == +1 ? MV_DOWN  : MV_NONE);
14213   boolean can_continue_snapping = (level.continuous_snapping &&
14214                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14215
14216   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14217     return FALSE;
14218
14219   if (!player->active || !IN_LEV_FIELD(x, y))
14220     return FALSE;
14221
14222   if (dx && dy)
14223     return FALSE;
14224
14225   if (!dx && !dy)
14226   {
14227     if (player->MovPos == 0)
14228       player->is_pushing = FALSE;
14229
14230     player->is_snapping = FALSE;
14231
14232     if (player->MovPos == 0)
14233     {
14234       player->is_moving = FALSE;
14235       player->is_digging = FALSE;
14236       player->is_collecting = FALSE;
14237     }
14238
14239     return FALSE;
14240   }
14241
14242   /* prevent snapping with already pressed snap key when not allowed */
14243   if (player->is_snapping && !can_continue_snapping)
14244     return FALSE;
14245
14246   player->MovDir = snap_direction;
14247
14248   if (player->MovPos == 0)
14249   {
14250     player->is_moving = FALSE;
14251     player->is_digging = FALSE;
14252     player->is_collecting = FALSE;
14253   }
14254
14255   player->is_dropping = FALSE;
14256   player->is_dropping_pressed = FALSE;
14257   player->drop_pressed_delay = 0;
14258
14259   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14260     return FALSE;
14261
14262   player->is_snapping = TRUE;
14263   player->is_active = TRUE;
14264
14265   if (player->MovPos == 0)
14266   {
14267     player->is_moving = FALSE;
14268     player->is_digging = FALSE;
14269     player->is_collecting = FALSE;
14270   }
14271
14272   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14273     TEST_DrawLevelField(player->last_jx, player->last_jy);
14274
14275   TEST_DrawLevelField(x, y);
14276
14277   return TRUE;
14278 }
14279
14280 static boolean DropElement(struct PlayerInfo *player)
14281 {
14282   int old_element, new_element;
14283   int dropx = player->jx, dropy = player->jy;
14284   int drop_direction = player->MovDir;
14285   int drop_side = drop_direction;
14286   int drop_element = get_next_dropped_element(player);
14287
14288   /* do not drop an element on top of another element; when holding drop key
14289      pressed without moving, dropped element must move away before the next
14290      element can be dropped (this is especially important if the next element
14291      is dynamite, which can be placed on background for historical reasons) */
14292   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14293     return MP_ACTION;
14294
14295   if (IS_THROWABLE(drop_element))
14296   {
14297     dropx += GET_DX_FROM_DIR(drop_direction);
14298     dropy += GET_DY_FROM_DIR(drop_direction);
14299
14300     if (!IN_LEV_FIELD(dropx, dropy))
14301       return FALSE;
14302   }
14303
14304   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14305   new_element = drop_element;           /* default: no change when dropping */
14306
14307   /* check if player is active, not moving and ready to drop */
14308   if (!player->active || player->MovPos || player->drop_delay > 0)
14309     return FALSE;
14310
14311   /* check if player has anything that can be dropped */
14312   if (new_element == EL_UNDEFINED)
14313     return FALSE;
14314
14315   /* only set if player has anything that can be dropped */
14316   player->is_dropping_pressed = TRUE;
14317
14318   /* check if drop key was pressed long enough for EM style dynamite */
14319   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14320     return FALSE;
14321
14322   /* check if anything can be dropped at the current position */
14323   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14324     return FALSE;
14325
14326   /* collected custom elements can only be dropped on empty fields */
14327   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14328     return FALSE;
14329
14330   if (old_element != EL_EMPTY)
14331     Back[dropx][dropy] = old_element;   /* store old element on this field */
14332
14333   ResetGfxAnimation(dropx, dropy);
14334   ResetRandomAnimationValue(dropx, dropy);
14335
14336   if (player->inventory_size > 0 ||
14337       player->inventory_infinite_element != EL_UNDEFINED)
14338   {
14339     if (player->inventory_size > 0)
14340     {
14341       player->inventory_size--;
14342
14343       DrawGameDoorValues();
14344
14345       if (new_element == EL_DYNAMITE)
14346         new_element = EL_DYNAMITE_ACTIVE;
14347       else if (new_element == EL_EM_DYNAMITE)
14348         new_element = EL_EM_DYNAMITE_ACTIVE;
14349       else if (new_element == EL_SP_DISK_RED)
14350         new_element = EL_SP_DISK_RED_ACTIVE;
14351     }
14352
14353     Feld[dropx][dropy] = new_element;
14354
14355     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14356       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14357                           el2img(Feld[dropx][dropy]), 0);
14358
14359     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14360
14361     /* needed if previous element just changed to "empty" in the last frame */
14362     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14363
14364     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14365                                player->index_bit, drop_side);
14366     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14367                                         CE_PLAYER_DROPS_X,
14368                                         player->index_bit, drop_side);
14369
14370     TestIfElementTouchesCustomElement(dropx, dropy);
14371   }
14372   else          /* player is dropping a dyna bomb */
14373   {
14374     player->dynabombs_left--;
14375
14376     Feld[dropx][dropy] = new_element;
14377
14378     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14379       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14380                           el2img(Feld[dropx][dropy]), 0);
14381
14382     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14383   }
14384
14385   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14386     InitField_WithBug1(dropx, dropy, FALSE);
14387
14388   new_element = Feld[dropx][dropy];     /* element might have changed */
14389
14390   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14391       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14392   {
14393     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14394       MovDir[dropx][dropy] = drop_direction;
14395
14396     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14397
14398     /* do not cause impact style collision by dropping elements that can fall */
14399     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14400   }
14401
14402   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14403   player->is_dropping = TRUE;
14404
14405   player->drop_pressed_delay = 0;
14406   player->is_dropping_pressed = FALSE;
14407
14408   player->drop_x = dropx;
14409   player->drop_y = dropy;
14410
14411   return TRUE;
14412 }
14413
14414 /* ------------------------------------------------------------------------- */
14415 /* game sound playing functions                                              */
14416 /* ------------------------------------------------------------------------- */
14417
14418 static int *loop_sound_frame = NULL;
14419 static int *loop_sound_volume = NULL;
14420
14421 void InitPlayLevelSound()
14422 {
14423   int num_sounds = getSoundListSize();
14424
14425   checked_free(loop_sound_frame);
14426   checked_free(loop_sound_volume);
14427
14428   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14429   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14430 }
14431
14432 static void PlayLevelSound(int x, int y, int nr)
14433 {
14434   int sx = SCREENX(x), sy = SCREENY(y);
14435   int volume, stereo_position;
14436   int max_distance = 8;
14437   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14438
14439   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14440       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14441     return;
14442
14443   if (!IN_LEV_FIELD(x, y) ||
14444       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14445       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14446     return;
14447
14448   volume = SOUND_MAX_VOLUME;
14449
14450   if (!IN_SCR_FIELD(sx, sy))
14451   {
14452     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14453     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14454
14455     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14456   }
14457
14458   stereo_position = (SOUND_MAX_LEFT +
14459                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14460                      (SCR_FIELDX + 2 * max_distance));
14461
14462   if (IS_LOOP_SOUND(nr))
14463   {
14464     /* This assures that quieter loop sounds do not overwrite louder ones,
14465        while restarting sound volume comparison with each new game frame. */
14466
14467     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14468       return;
14469
14470     loop_sound_volume[nr] = volume;
14471     loop_sound_frame[nr] = FrameCounter;
14472   }
14473
14474   PlaySoundExt(nr, volume, stereo_position, type);
14475 }
14476
14477 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14478 {
14479   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14480                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14481                  y < LEVELY(BY1) ? LEVELY(BY1) :
14482                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14483                  sound_action);
14484 }
14485
14486 static void PlayLevelSoundAction(int x, int y, int action)
14487 {
14488   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14489 }
14490
14491 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14492 {
14493   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14494
14495   if (sound_effect != SND_UNDEFINED)
14496     PlayLevelSound(x, y, sound_effect);
14497 }
14498
14499 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14500                                               int action)
14501 {
14502   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14503
14504   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14505     PlayLevelSound(x, y, sound_effect);
14506 }
14507
14508 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14509 {
14510   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14511
14512   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14513     PlayLevelSound(x, y, sound_effect);
14514 }
14515
14516 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14517 {
14518   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14519
14520   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14521     StopSound(sound_effect);
14522 }
14523
14524 static int getLevelMusicNr()
14525 {
14526   if (levelset.music[level_nr] != MUS_UNDEFINED)
14527     return levelset.music[level_nr];            /* from config file */
14528   else
14529     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14530 }
14531
14532 static void FadeLevelSounds()
14533 {
14534   FadeSounds();
14535 }
14536
14537 static void FadeLevelMusic()
14538 {
14539   int music_nr = getLevelMusicNr();
14540   char *curr_music = getCurrentlyPlayingMusicFilename();
14541   char *next_music = getMusicInfoEntryFilename(music_nr);
14542
14543   if (!strEqual(curr_music, next_music))
14544     FadeMusic();
14545 }
14546
14547 void FadeLevelSoundsAndMusic()
14548 {
14549   FadeLevelSounds();
14550   FadeLevelMusic();
14551 }
14552
14553 static void PlayLevelMusic()
14554 {
14555   int music_nr = getLevelMusicNr();
14556   char *curr_music = getCurrentlyPlayingMusicFilename();
14557   char *next_music = getMusicInfoEntryFilename(music_nr);
14558
14559   if (!strEqual(curr_music, next_music))
14560     PlayMusic(music_nr);
14561 }
14562
14563 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14564 {
14565   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14566   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14567   int x = xx - 1 - offset;
14568   int y = yy - 1 - offset;
14569
14570   switch (sample)
14571   {
14572     case SAMPLE_blank:
14573       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14574       break;
14575
14576     case SAMPLE_roll:
14577       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14578       break;
14579
14580     case SAMPLE_stone:
14581       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14582       break;
14583
14584     case SAMPLE_nut:
14585       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14586       break;
14587
14588     case SAMPLE_crack:
14589       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14590       break;
14591
14592     case SAMPLE_bug:
14593       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14594       break;
14595
14596     case SAMPLE_tank:
14597       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14598       break;
14599
14600     case SAMPLE_android_clone:
14601       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14602       break;
14603
14604     case SAMPLE_android_move:
14605       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14606       break;
14607
14608     case SAMPLE_spring:
14609       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14610       break;
14611
14612     case SAMPLE_slurp:
14613       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14614       break;
14615
14616     case SAMPLE_eater:
14617       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14618       break;
14619
14620     case SAMPLE_eater_eat:
14621       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14622       break;
14623
14624     case SAMPLE_alien:
14625       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14626       break;
14627
14628     case SAMPLE_collect:
14629       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14630       break;
14631
14632     case SAMPLE_diamond:
14633       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14634       break;
14635
14636     case SAMPLE_squash:
14637       /* !!! CHECK THIS !!! */
14638 #if 1
14639       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14640 #else
14641       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14642 #endif
14643       break;
14644
14645     case SAMPLE_wonderfall:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14647       break;
14648
14649     case SAMPLE_drip:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14651       break;
14652
14653     case SAMPLE_push:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14655       break;
14656
14657     case SAMPLE_dirt:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14659       break;
14660
14661     case SAMPLE_acid:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14663       break;
14664
14665     case SAMPLE_ball:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14667       break;
14668
14669     case SAMPLE_grow:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14671       break;
14672
14673     case SAMPLE_wonder:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14675       break;
14676
14677     case SAMPLE_door:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14679       break;
14680
14681     case SAMPLE_exit_open:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14683       break;
14684
14685     case SAMPLE_exit_leave:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14687       break;
14688
14689     case SAMPLE_dynamite:
14690       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14691       break;
14692
14693     case SAMPLE_tick:
14694       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14695       break;
14696
14697     case SAMPLE_press:
14698       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14699       break;
14700
14701     case SAMPLE_wheel:
14702       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14703       break;
14704
14705     case SAMPLE_boom:
14706       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14707       break;
14708
14709     case SAMPLE_die:
14710       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14711       break;
14712
14713     case SAMPLE_time:
14714       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14715       break;
14716
14717     default:
14718       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14719       break;
14720   }
14721 }
14722
14723 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14724 {
14725   int element = map_element_SP_to_RND(element_sp);
14726   int action = map_action_SP_to_RND(action_sp);
14727   int offset = (setup.sp_show_border_elements ? 0 : 1);
14728   int x = xx - offset;
14729   int y = yy - offset;
14730
14731   PlayLevelSoundElementAction(x, y, element, action);
14732 }
14733
14734 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14735 {
14736   int element = map_element_MM_to_RND(element_mm);
14737   int action = map_action_MM_to_RND(action_mm);
14738   int offset = 0;
14739   int x = xx - offset;
14740   int y = yy - offset;
14741
14742   if (!IS_MM_ELEMENT(element))
14743     element = EL_MM_DEFAULT;
14744
14745   PlayLevelSoundElementAction(x, y, element, action);
14746 }
14747
14748 void PlaySound_MM(int sound_mm)
14749 {
14750   int sound = map_sound_MM_to_RND(sound_mm);
14751
14752   if (sound == SND_UNDEFINED)
14753     return;
14754
14755   PlaySound(sound);
14756 }
14757
14758 void PlaySoundLoop_MM(int sound_mm)
14759 {
14760   int sound = map_sound_MM_to_RND(sound_mm);
14761
14762   if (sound == SND_UNDEFINED)
14763     return;
14764
14765   PlaySoundLoop(sound);
14766 }
14767
14768 void StopSound_MM(int sound_mm)
14769 {
14770   int sound = map_sound_MM_to_RND(sound_mm);
14771
14772   if (sound == SND_UNDEFINED)
14773     return;
14774
14775   StopSound(sound);
14776 }
14777
14778 void RaiseScore(int value)
14779 {
14780   local_player->score += value;
14781
14782   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14783
14784   DisplayGameControlValues();
14785 }
14786
14787 void RaiseScoreElement(int element)
14788 {
14789   switch (element)
14790   {
14791     case EL_EMERALD:
14792     case EL_BD_DIAMOND:
14793     case EL_EMERALD_YELLOW:
14794     case EL_EMERALD_RED:
14795     case EL_EMERALD_PURPLE:
14796     case EL_SP_INFOTRON:
14797       RaiseScore(level.score[SC_EMERALD]);
14798       break;
14799     case EL_DIAMOND:
14800       RaiseScore(level.score[SC_DIAMOND]);
14801       break;
14802     case EL_CRYSTAL:
14803       RaiseScore(level.score[SC_CRYSTAL]);
14804       break;
14805     case EL_PEARL:
14806       RaiseScore(level.score[SC_PEARL]);
14807       break;
14808     case EL_BUG:
14809     case EL_BD_BUTTERFLY:
14810     case EL_SP_ELECTRON:
14811       RaiseScore(level.score[SC_BUG]);
14812       break;
14813     case EL_SPACESHIP:
14814     case EL_BD_FIREFLY:
14815     case EL_SP_SNIKSNAK:
14816       RaiseScore(level.score[SC_SPACESHIP]);
14817       break;
14818     case EL_YAMYAM:
14819     case EL_DARK_YAMYAM:
14820       RaiseScore(level.score[SC_YAMYAM]);
14821       break;
14822     case EL_ROBOT:
14823       RaiseScore(level.score[SC_ROBOT]);
14824       break;
14825     case EL_PACMAN:
14826       RaiseScore(level.score[SC_PACMAN]);
14827       break;
14828     case EL_NUT:
14829       RaiseScore(level.score[SC_NUT]);
14830       break;
14831     case EL_DYNAMITE:
14832     case EL_EM_DYNAMITE:
14833     case EL_SP_DISK_RED:
14834     case EL_DYNABOMB_INCREASE_NUMBER:
14835     case EL_DYNABOMB_INCREASE_SIZE:
14836     case EL_DYNABOMB_INCREASE_POWER:
14837       RaiseScore(level.score[SC_DYNAMITE]);
14838       break;
14839     case EL_SHIELD_NORMAL:
14840     case EL_SHIELD_DEADLY:
14841       RaiseScore(level.score[SC_SHIELD]);
14842       break;
14843     case EL_EXTRA_TIME:
14844       RaiseScore(level.extra_time_score);
14845       break;
14846     case EL_KEY_1:
14847     case EL_KEY_2:
14848     case EL_KEY_3:
14849     case EL_KEY_4:
14850     case EL_EM_KEY_1:
14851     case EL_EM_KEY_2:
14852     case EL_EM_KEY_3:
14853     case EL_EM_KEY_4:
14854     case EL_EMC_KEY_5:
14855     case EL_EMC_KEY_6:
14856     case EL_EMC_KEY_7:
14857     case EL_EMC_KEY_8:
14858     case EL_DC_KEY_WHITE:
14859       RaiseScore(level.score[SC_KEY]);
14860       break;
14861     default:
14862       RaiseScore(element_info[element].collect_score);
14863       break;
14864   }
14865 }
14866
14867 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14868 {
14869   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14870   {
14871     /* closing door required in case of envelope style request dialogs */
14872     if (!skip_request)
14873       CloseDoor(DOOR_CLOSE_1);
14874
14875     if (network.enabled)
14876       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14877     else
14878     {
14879       if (quick_quit)
14880         FadeSkipNextFadeIn();
14881
14882       SetGameStatus(GAME_MODE_MAIN);
14883
14884       DrawMainMenu();
14885     }
14886   }
14887   else          /* continue playing the game */
14888   {
14889     if (tape.playing && tape.deactivate_display)
14890       TapeDeactivateDisplayOff(TRUE);
14891
14892     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14893
14894     if (tape.playing && tape.deactivate_display)
14895       TapeDeactivateDisplayOn();
14896   }
14897 }
14898
14899 void RequestQuitGame(boolean ask_if_really_quit)
14900 {
14901   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14902   boolean skip_request = AllPlayersGone || quick_quit;
14903
14904   RequestQuitGameExt(skip_request, quick_quit,
14905                      "Do you really want to quit the game?");
14906 }
14907
14908 void RequestRestartGame(char *message)
14909 {
14910   game.restart_game_message = NULL;
14911
14912   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14913   {
14914     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14915   }
14916   else
14917   {
14918     SetGameStatus(GAME_MODE_MAIN);
14919
14920     DrawMainMenu();
14921   }
14922 }
14923
14924
14925 /* ------------------------------------------------------------------------- */
14926 /* random generator functions                                                */
14927 /* ------------------------------------------------------------------------- */
14928
14929 unsigned int InitEngineRandom_RND(int seed)
14930 {
14931   game.num_random_calls = 0;
14932
14933   return InitEngineRandom(seed);
14934 }
14935
14936 unsigned int RND(int max)
14937 {
14938   if (max > 0)
14939   {
14940     game.num_random_calls++;
14941
14942     return GetEngineRandom(max);
14943   }
14944
14945   return 0;
14946 }
14947
14948
14949 /* ------------------------------------------------------------------------- */
14950 /* game engine snapshot handling functions                                   */
14951 /* ------------------------------------------------------------------------- */
14952
14953 struct EngineSnapshotInfo
14954 {
14955   /* runtime values for custom element collect score */
14956   int collect_score[NUM_CUSTOM_ELEMENTS];
14957
14958   /* runtime values for group element choice position */
14959   int choice_pos[NUM_GROUP_ELEMENTS];
14960
14961   /* runtime values for belt position animations */
14962   int belt_graphic[4][NUM_BELT_PARTS];
14963   int belt_anim_mode[4][NUM_BELT_PARTS];
14964 };
14965
14966 static struct EngineSnapshotInfo engine_snapshot_rnd;
14967 static char *snapshot_level_identifier = NULL;
14968 static int snapshot_level_nr = -1;
14969
14970 static void SaveEngineSnapshotValues_RND()
14971 {
14972   static int belt_base_active_element[4] =
14973   {
14974     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14975     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14976     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14977     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14978   };
14979   int i, j;
14980
14981   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14982   {
14983     int element = EL_CUSTOM_START + i;
14984
14985     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14986   }
14987
14988   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14989   {
14990     int element = EL_GROUP_START + i;
14991
14992     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14993   }
14994
14995   for (i = 0; i < 4; i++)
14996   {
14997     for (j = 0; j < NUM_BELT_PARTS; j++)
14998     {
14999       int element = belt_base_active_element[i] + j;
15000       int graphic = el2img(element);
15001       int anim_mode = graphic_info[graphic].anim_mode;
15002
15003       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15004       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15005     }
15006   }
15007 }
15008
15009 static void LoadEngineSnapshotValues_RND()
15010 {
15011   unsigned int num_random_calls = game.num_random_calls;
15012   int i, j;
15013
15014   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15015   {
15016     int element = EL_CUSTOM_START + i;
15017
15018     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15019   }
15020
15021   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15022   {
15023     int element = EL_GROUP_START + i;
15024
15025     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15026   }
15027
15028   for (i = 0; i < 4; i++)
15029   {
15030     for (j = 0; j < NUM_BELT_PARTS; j++)
15031     {
15032       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15033       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15034
15035       graphic_info[graphic].anim_mode = anim_mode;
15036     }
15037   }
15038
15039   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15040   {
15041     InitRND(tape.random_seed);
15042     for (i = 0; i < num_random_calls; i++)
15043       RND(1);
15044   }
15045
15046   if (game.num_random_calls != num_random_calls)
15047   {
15048     Error(ERR_INFO, "number of random calls out of sync");
15049     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15050     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15051     Error(ERR_EXIT, "this should not happen -- please debug");
15052   }
15053 }
15054
15055 void FreeEngineSnapshotSingle()
15056 {
15057   FreeSnapshotSingle();
15058
15059   setString(&snapshot_level_identifier, NULL);
15060   snapshot_level_nr = -1;
15061 }
15062
15063 void FreeEngineSnapshotList()
15064 {
15065   FreeSnapshotList();
15066 }
15067
15068 ListNode *SaveEngineSnapshotBuffers()
15069 {
15070   ListNode *buffers = NULL;
15071
15072   /* copy some special values to a structure better suited for the snapshot */
15073
15074   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15075     SaveEngineSnapshotValues_RND();
15076   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15077     SaveEngineSnapshotValues_EM();
15078   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15079     SaveEngineSnapshotValues_SP(&buffers);
15080   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15081     SaveEngineSnapshotValues_MM(&buffers);
15082
15083   /* save values stored in special snapshot structure */
15084
15085   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15086     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15087   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15088     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15089   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15090     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15091   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15092     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15093
15094   /* save further RND engine values */
15095
15096   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15097   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15098   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15099
15100   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15101   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15102   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15103   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15104
15105   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15106   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15107   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15108   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15109   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15110
15111   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15112   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15113   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15114
15115   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15116
15117   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15118
15119   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15120   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15121
15122   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15123   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15125   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15126   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15127   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15128   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15131   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15137   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15140
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15143
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15147
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15150
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15156
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15159
15160 #if 0
15161   ListNode *node = engine_snapshot_list_rnd;
15162   int num_bytes = 0;
15163
15164   while (node != NULL)
15165   {
15166     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15167
15168     node = node->next;
15169   }
15170
15171   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15172 #endif
15173
15174   return buffers;
15175 }
15176
15177 void SaveEngineSnapshotSingle()
15178 {
15179   ListNode *buffers = SaveEngineSnapshotBuffers();
15180
15181   /* finally save all snapshot buffers to single snapshot */
15182   SaveSnapshotSingle(buffers);
15183
15184   /* save level identification information */
15185   setString(&snapshot_level_identifier, leveldir_current->identifier);
15186   snapshot_level_nr = level_nr;
15187 }
15188
15189 boolean CheckSaveEngineSnapshotToList()
15190 {
15191   boolean save_snapshot =
15192     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15193      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15194       game.snapshot.changed_action) ||
15195      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15196       game.snapshot.collected_item));
15197
15198   game.snapshot.changed_action = FALSE;
15199   game.snapshot.collected_item = FALSE;
15200   game.snapshot.save_snapshot = save_snapshot;
15201
15202   return save_snapshot;
15203 }
15204
15205 void SaveEngineSnapshotToList()
15206 {
15207   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15208       tape.quick_resume)
15209     return;
15210
15211   ListNode *buffers = SaveEngineSnapshotBuffers();
15212
15213   /* finally save all snapshot buffers to snapshot list */
15214   SaveSnapshotToList(buffers);
15215 }
15216
15217 void SaveEngineSnapshotToListInitial()
15218 {
15219   FreeEngineSnapshotList();
15220
15221   SaveEngineSnapshotToList();
15222 }
15223
15224 void LoadEngineSnapshotValues()
15225 {
15226   /* restore special values from snapshot structure */
15227
15228   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15229     LoadEngineSnapshotValues_RND();
15230   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15231     LoadEngineSnapshotValues_EM();
15232   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15233     LoadEngineSnapshotValues_SP();
15234   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15235     LoadEngineSnapshotValues_MM();
15236 }
15237
15238 void LoadEngineSnapshotSingle()
15239 {
15240   LoadSnapshotSingle();
15241
15242   LoadEngineSnapshotValues();
15243 }
15244
15245 void LoadEngineSnapshot_Undo(int steps)
15246 {
15247   LoadSnapshotFromList_Older(steps);
15248
15249   LoadEngineSnapshotValues();
15250 }
15251
15252 void LoadEngineSnapshot_Redo(int steps)
15253 {
15254   LoadSnapshotFromList_Newer(steps);
15255
15256   LoadEngineSnapshotValues();
15257 }
15258
15259 boolean CheckEngineSnapshotSingle()
15260 {
15261   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15262           snapshot_level_nr == level_nr);
15263 }
15264
15265 boolean CheckEngineSnapshotList()
15266 {
15267   return CheckSnapshotList();
15268 }
15269
15270
15271 /* ---------- new game button stuff ---------------------------------------- */
15272
15273 static struct
15274 {
15275   int graphic;
15276   struct XY *pos;
15277   int gadget_id;
15278   boolean *setup_value;
15279   boolean allowed_on_tape;
15280   char *infotext;
15281 } gamebutton_info[NUM_GAME_BUTTONS] =
15282 {
15283   {
15284     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15285     GAME_CTRL_ID_STOP,                          NULL,
15286     TRUE,                                       "stop game"
15287   },
15288   {
15289     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15290     GAME_CTRL_ID_PAUSE,                         NULL,
15291     TRUE,                                       "pause game"
15292   },
15293   {
15294     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15295     GAME_CTRL_ID_PLAY,                          NULL,
15296     TRUE,                                       "play game"
15297   },
15298   {
15299     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15300     GAME_CTRL_ID_UNDO,                          NULL,
15301     TRUE,                                       "undo step"
15302   },
15303   {
15304     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15305     GAME_CTRL_ID_REDO,                          NULL,
15306     TRUE,                                       "redo step"
15307   },
15308   {
15309     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15310     GAME_CTRL_ID_SAVE,                          NULL,
15311     TRUE,                                       "save game"
15312   },
15313   {
15314     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15315     GAME_CTRL_ID_PAUSE2,                        NULL,
15316     TRUE,                                       "pause game"
15317   },
15318   {
15319     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15320     GAME_CTRL_ID_LOAD,                          NULL,
15321     TRUE,                                       "load game"
15322   },
15323   {
15324     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15325     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15326     FALSE,                                      "stop game"
15327   },
15328   {
15329     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15330     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15331     FALSE,                                      "pause game"
15332   },
15333   {
15334     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15335     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15336     FALSE,                                      "play game"
15337   },
15338   {
15339     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15340     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15341     TRUE,                                       "background music on/off"
15342   },
15343   {
15344     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15345     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15346     TRUE,                                       "sound loops on/off"
15347   },
15348   {
15349     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15350     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15351     TRUE,                                       "normal sounds on/off"
15352   },
15353   {
15354     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15355     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15356     FALSE,                                      "background music on/off"
15357   },
15358   {
15359     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15360     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15361     FALSE,                                      "sound loops on/off"
15362   },
15363   {
15364     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15365     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15366     FALSE,                                      "normal sounds on/off"
15367   }
15368 };
15369
15370 void CreateGameButtons()
15371 {
15372   int i;
15373
15374   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15375   {
15376     int graphic = gamebutton_info[i].graphic;
15377     struct GraphicInfo *gfx = &graphic_info[graphic];
15378     struct XY *pos = gamebutton_info[i].pos;
15379     struct GadgetInfo *gi;
15380     int button_type;
15381     boolean checked;
15382     unsigned int event_mask;
15383     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15384     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15385     int base_x = (on_tape ? VX : DX);
15386     int base_y = (on_tape ? VY : DY);
15387     int gd_x   = gfx->src_x;
15388     int gd_y   = gfx->src_y;
15389     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15390     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15391     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15392     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15393     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15394     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15395     int id = i;
15396
15397     if (gfx->bitmap == NULL)
15398     {
15399       game_gadget[id] = NULL;
15400
15401       continue;
15402     }
15403
15404     if (id == GAME_CTRL_ID_STOP ||
15405         id == GAME_CTRL_ID_PANEL_STOP ||
15406         id == GAME_CTRL_ID_PLAY ||
15407         id == GAME_CTRL_ID_PANEL_PLAY ||
15408         id == GAME_CTRL_ID_SAVE ||
15409         id == GAME_CTRL_ID_LOAD)
15410     {
15411       button_type = GD_TYPE_NORMAL_BUTTON;
15412       checked = FALSE;
15413       event_mask = GD_EVENT_RELEASED;
15414     }
15415     else if (id == GAME_CTRL_ID_UNDO ||
15416              id == GAME_CTRL_ID_REDO)
15417     {
15418       button_type = GD_TYPE_NORMAL_BUTTON;
15419       checked = FALSE;
15420       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15421     }
15422     else
15423     {
15424       button_type = GD_TYPE_CHECK_BUTTON;
15425       checked = (gamebutton_info[i].setup_value != NULL ?
15426                  *gamebutton_info[i].setup_value : FALSE);
15427       event_mask = GD_EVENT_PRESSED;
15428     }
15429
15430     gi = CreateGadget(GDI_CUSTOM_ID, id,
15431                       GDI_IMAGE_ID, graphic,
15432                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15433                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15434                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15435                       GDI_WIDTH, gfx->width,
15436                       GDI_HEIGHT, gfx->height,
15437                       GDI_TYPE, button_type,
15438                       GDI_STATE, GD_BUTTON_UNPRESSED,
15439                       GDI_CHECKED, checked,
15440                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15441                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15442                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15443                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15444                       GDI_DIRECT_DRAW, FALSE,
15445                       GDI_EVENT_MASK, event_mask,
15446                       GDI_CALLBACK_ACTION, HandleGameButtons,
15447                       GDI_END);
15448
15449     if (gi == NULL)
15450       Error(ERR_EXIT, "cannot create gadget");
15451
15452     game_gadget[id] = gi;
15453   }
15454 }
15455
15456 void FreeGameButtons()
15457 {
15458   int i;
15459
15460   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15461     FreeGadget(game_gadget[i]);
15462 }
15463
15464 static void UnmapGameButtonsAtSamePosition(int id)
15465 {
15466   int i;
15467
15468   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15469     if (i != id &&
15470         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15471         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15472       UnmapGadget(game_gadget[i]);
15473 }
15474
15475 static void UnmapGameButtonsAtSamePosition_All()
15476 {
15477   if (setup.show_snapshot_buttons)
15478   {
15479     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15480     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15481     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15482   }
15483   else
15484   {
15485     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15486     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15487     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15488
15489     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15490     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15491     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15492   }
15493 }
15494
15495 static void MapGameButtonsAtSamePosition(int id)
15496 {
15497   int i;
15498
15499   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15500     if (i != id &&
15501         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15502         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15503       MapGadget(game_gadget[i]);
15504
15505   UnmapGameButtonsAtSamePosition_All();
15506 }
15507
15508 void MapUndoRedoButtons()
15509 {
15510   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15511   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15512
15513   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15514   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15515
15516   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15517 }
15518
15519 void UnmapUndoRedoButtons()
15520 {
15521   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15522   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15523
15524   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15525   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15526
15527   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15528 }
15529
15530 void MapGameButtonsExt(boolean on_tape)
15531 {
15532   int i;
15533
15534   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15535     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15536         i != GAME_CTRL_ID_UNDO &&
15537         i != GAME_CTRL_ID_REDO)
15538       MapGadget(game_gadget[i]);
15539
15540   UnmapGameButtonsAtSamePosition_All();
15541
15542   RedrawGameButtons();
15543 }
15544
15545 void UnmapGameButtonsExt(boolean on_tape)
15546 {
15547   int i;
15548
15549   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15550     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15551       UnmapGadget(game_gadget[i]);
15552 }
15553
15554 void RedrawGameButtonsExt(boolean on_tape)
15555 {
15556   int i;
15557
15558   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15559     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15560       RedrawGadget(game_gadget[i]);
15561
15562   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15563   redraw_mask &= ~REDRAW_ALL;
15564 }
15565
15566 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15567 {
15568   if (gi == NULL)
15569     return;
15570
15571   gi->checked = state;
15572 }
15573
15574 void RedrawSoundButtonGadget(int id)
15575 {
15576   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15577              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15578              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15579              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15580              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15581              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15582              id);
15583
15584   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15585   RedrawGadget(game_gadget[id2]);
15586 }
15587
15588 void MapGameButtons()
15589 {
15590   MapGameButtonsExt(FALSE);
15591 }
15592
15593 void UnmapGameButtons()
15594 {
15595   UnmapGameButtonsExt(FALSE);
15596 }
15597
15598 void RedrawGameButtons()
15599 {
15600   RedrawGameButtonsExt(FALSE);
15601 }
15602
15603 void MapGameButtonsOnTape()
15604 {
15605   MapGameButtonsExt(TRUE);
15606 }
15607
15608 void UnmapGameButtonsOnTape()
15609 {
15610   UnmapGameButtonsExt(TRUE);
15611 }
15612
15613 void RedrawGameButtonsOnTape()
15614 {
15615   RedrawGameButtonsExt(TRUE);
15616 }
15617
15618 void GameUndoRedoExt()
15619 {
15620   ClearPlayerAction();
15621
15622   tape.pausing = TRUE;
15623
15624   RedrawPlayfield();
15625   UpdateAndDisplayGameControlValues();
15626
15627   DrawCompleteVideoDisplay();
15628   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15629   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15630   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15631
15632   BackToFront();
15633 }
15634
15635 void GameUndo(int steps)
15636 {
15637   if (!CheckEngineSnapshotList())
15638     return;
15639
15640   LoadEngineSnapshot_Undo(steps);
15641
15642   GameUndoRedoExt();
15643 }
15644
15645 void GameRedo(int steps)
15646 {
15647   if (!CheckEngineSnapshotList())
15648     return;
15649
15650   LoadEngineSnapshot_Redo(steps);
15651
15652   GameUndoRedoExt();
15653 }
15654
15655 static void HandleGameButtonsExt(int id, int button)
15656 {
15657   static boolean game_undo_executed = FALSE;
15658   int steps = BUTTON_STEPSIZE(button);
15659   boolean handle_game_buttons =
15660     (game_status == GAME_MODE_PLAYING ||
15661      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15662
15663   if (!handle_game_buttons)
15664     return;
15665
15666   switch (id)
15667   {
15668     case GAME_CTRL_ID_STOP:
15669     case GAME_CTRL_ID_PANEL_STOP:
15670       if (game_status == GAME_MODE_MAIN)
15671         break;
15672
15673       if (tape.playing)
15674         TapeStop();
15675       else
15676         RequestQuitGame(TRUE);
15677
15678       break;
15679
15680     case GAME_CTRL_ID_PAUSE:
15681     case GAME_CTRL_ID_PAUSE2:
15682     case GAME_CTRL_ID_PANEL_PAUSE:
15683       if (network.enabled && game_status == GAME_MODE_PLAYING)
15684       {
15685         if (tape.pausing)
15686           SendToServer_ContinuePlaying();
15687         else
15688           SendToServer_PausePlaying();
15689       }
15690       else
15691         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15692
15693       game_undo_executed = FALSE;
15694
15695       break;
15696
15697     case GAME_CTRL_ID_PLAY:
15698     case GAME_CTRL_ID_PANEL_PLAY:
15699       if (game_status == GAME_MODE_MAIN)
15700       {
15701         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15702       }
15703       else if (tape.pausing)
15704       {
15705         if (network.enabled)
15706           SendToServer_ContinuePlaying();
15707         else
15708           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15709       }
15710       break;
15711
15712     case GAME_CTRL_ID_UNDO:
15713       // Important: When using "save snapshot when collecting an item" mode,
15714       // load last (current) snapshot for first "undo" after pressing "pause"
15715       // (else the last-but-one snapshot would be loaded, because the snapshot
15716       // pointer already points to the last snapshot when pressing "pause",
15717       // which is fine for "every step/move" mode, but not for "every collect")
15718       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15719           !game_undo_executed)
15720         steps--;
15721
15722       game_undo_executed = TRUE;
15723
15724       GameUndo(steps);
15725       break;
15726
15727     case GAME_CTRL_ID_REDO:
15728       GameRedo(steps);
15729       break;
15730
15731     case GAME_CTRL_ID_SAVE:
15732       TapeQuickSave();
15733       break;
15734
15735     case GAME_CTRL_ID_LOAD:
15736       TapeQuickLoad();
15737       break;
15738
15739     case SOUND_CTRL_ID_MUSIC:
15740     case SOUND_CTRL_ID_PANEL_MUSIC:
15741       if (setup.sound_music)
15742       { 
15743         setup.sound_music = FALSE;
15744
15745         FadeMusic();
15746       }
15747       else if (audio.music_available)
15748       { 
15749         setup.sound = setup.sound_music = TRUE;
15750
15751         SetAudioMode(setup.sound);
15752
15753         if (game_status == GAME_MODE_PLAYING)
15754           PlayLevelMusic();
15755       }
15756
15757       RedrawSoundButtonGadget(id);
15758
15759       break;
15760
15761     case SOUND_CTRL_ID_LOOPS:
15762     case SOUND_CTRL_ID_PANEL_LOOPS:
15763       if (setup.sound_loops)
15764         setup.sound_loops = FALSE;
15765       else if (audio.loops_available)
15766       {
15767         setup.sound = setup.sound_loops = TRUE;
15768
15769         SetAudioMode(setup.sound);
15770       }
15771
15772       RedrawSoundButtonGadget(id);
15773
15774       break;
15775
15776     case SOUND_CTRL_ID_SIMPLE:
15777     case SOUND_CTRL_ID_PANEL_SIMPLE:
15778       if (setup.sound_simple)
15779         setup.sound_simple = FALSE;
15780       else if (audio.sound_available)
15781       {
15782         setup.sound = setup.sound_simple = TRUE;
15783
15784         SetAudioMode(setup.sound);
15785       }
15786
15787       RedrawSoundButtonGadget(id);
15788
15789       break;
15790
15791     default:
15792       break;
15793   }
15794 }
15795
15796 static void HandleGameButtons(struct GadgetInfo *gi)
15797 {
15798   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15799 }
15800
15801 void HandleSoundButtonKeys(Key key)
15802 {
15803   if (key == setup.shortcut.sound_simple)
15804     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15805   else if (key == setup.shortcut.sound_loops)
15806     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15807   else if (key == setup.shortcut.sound_music)
15808     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15809 }