renamed function
[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 void ExitPlayer(struct PlayerInfo *);
1116
1117 static int getInvisibleActiveFromInvisibleElement(int);
1118 static int getInvisibleFromInvisibleActiveElement(int);
1119
1120 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1121
1122 /* for detection of endless loops, caused by custom element programming */
1123 /* (using maximal playfield width x 10 is just a rough approximation) */
1124 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1125
1126 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1127 {                                                                       \
1128   if (recursion_loop_detected)                                          \
1129     return (rc);                                                        \
1130                                                                         \
1131   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1132   {                                                                     \
1133     recursion_loop_detected = TRUE;                                     \
1134     recursion_loop_element = (e);                                       \
1135   }                                                                     \
1136                                                                         \
1137   recursion_loop_depth++;                                               \
1138 }
1139
1140 #define RECURSION_LOOP_DETECTION_END()                                  \
1141 {                                                                       \
1142   recursion_loop_depth--;                                               \
1143 }
1144
1145 static int recursion_loop_depth;
1146 static boolean recursion_loop_detected;
1147 static boolean recursion_loop_element;
1148
1149 static int map_player_action[MAX_PLAYERS];
1150
1151
1152 /* ------------------------------------------------------------------------- */
1153 /* definition of elements that automatically change to other elements after  */
1154 /* a specified time, eventually calling a function when changing             */
1155 /* ------------------------------------------------------------------------- */
1156
1157 /* forward declaration for changer functions */
1158 static void InitBuggyBase(int, int);
1159 static void WarnBuggyBase(int, int);
1160
1161 static void InitTrap(int, int);
1162 static void ActivateTrap(int, int);
1163 static void ChangeActiveTrap(int, int);
1164
1165 static void InitRobotWheel(int, int);
1166 static void RunRobotWheel(int, int);
1167 static void StopRobotWheel(int, int);
1168
1169 static void InitTimegateWheel(int, int);
1170 static void RunTimegateWheel(int, int);
1171
1172 static void InitMagicBallDelay(int, int);
1173 static void ActivateMagicBall(int, int);
1174
1175 struct ChangingElementInfo
1176 {
1177   int element;
1178   int target_element;
1179   int change_delay;
1180   void (*pre_change_function)(int x, int y);
1181   void (*change_function)(int x, int y);
1182   void (*post_change_function)(int x, int y);
1183 };
1184
1185 static struct ChangingElementInfo change_delay_list[] =
1186 {
1187   {
1188     EL_NUT_BREAKING,
1189     EL_EMERALD,
1190     6,
1191     NULL,
1192     NULL,
1193     NULL
1194   },
1195   {
1196     EL_PEARL_BREAKING,
1197     EL_EMPTY,
1198     8,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_EXIT_OPENING,
1205     EL_EXIT_OPEN,
1206     29,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_CLOSING,
1213     EL_EXIT_CLOSED,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_STEEL_EXIT_OPENING,
1221     EL_STEEL_EXIT_OPEN,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_CLOSING,
1229     EL_STEEL_EXIT_CLOSED,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_EM_EXIT_OPENING,
1237     EL_EM_EXIT_OPEN,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_CLOSING,
1245     EL_EMPTY,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_STEEL_EXIT_OPENING,
1253     EL_EM_STEEL_EXIT_OPEN,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_CLOSING,
1261     EL_STEELWALL,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_SP_EXIT_OPENING,
1269     EL_SP_EXIT_OPEN,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_CLOSING,
1277     EL_SP_EXIT_CLOSED,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SWITCHGATE_OPENING,
1285     EL_SWITCHGATE_OPEN,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_CLOSING,
1293     EL_SWITCHGATE_CLOSED,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_TIMEGATE_OPENING,
1301     EL_TIMEGATE_OPEN,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_CLOSING,
1309     EL_TIMEGATE_CLOSED,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315
1316   {
1317     EL_ACID_SPLASH_LEFT,
1318     EL_EMPTY,
1319     8,
1320     NULL,
1321     NULL,
1322     NULL
1323   },
1324   {
1325     EL_ACID_SPLASH_RIGHT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_SP_BUGGY_BASE,
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     0,
1336     InitBuggyBase,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE_ACTIVATING,
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVE,
1350     EL_SP_BUGGY_BASE,
1351     0,
1352     InitBuggyBase,
1353     WarnBuggyBase,
1354     NULL
1355   },
1356   {
1357     EL_TRAP,
1358     EL_TRAP_ACTIVE,
1359     0,
1360     InitTrap,
1361     NULL,
1362     ActivateTrap
1363   },
1364   {
1365     EL_TRAP_ACTIVE,
1366     EL_TRAP,
1367     31,
1368     NULL,
1369     ChangeActiveTrap,
1370     NULL
1371   },
1372   {
1373     EL_ROBOT_WHEEL_ACTIVE,
1374     EL_ROBOT_WHEEL,
1375     0,
1376     InitRobotWheel,
1377     RunRobotWheel,
1378     StopRobotWheel
1379   },
1380   {
1381     EL_TIMEGATE_SWITCH_ACTIVE,
1382     EL_TIMEGATE_SWITCH,
1383     0,
1384     InitTimegateWheel,
1385     RunTimegateWheel,
1386     NULL
1387   },
1388   {
1389     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1390     EL_DC_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_EMC_MAGIC_BALL_ACTIVE,
1398     EL_EMC_MAGIC_BALL_ACTIVE,
1399     0,
1400     InitMagicBallDelay,
1401     NULL,
1402     ActivateMagicBall
1403   },
1404   {
1405     EL_EMC_SPRING_BUMPER_ACTIVE,
1406     EL_EMC_SPRING_BUMPER,
1407     8,
1408     NULL,
1409     NULL,
1410     NULL
1411   },
1412   {
1413     EL_DIAGONAL_SHRINKING,
1414     EL_UNDEFINED,
1415     0,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_GROWING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL,
1427   },
1428
1429   {
1430     EL_UNDEFINED,
1431     EL_UNDEFINED,
1432     -1,
1433     NULL,
1434     NULL,
1435     NULL
1436   }
1437 };
1438
1439 struct
1440 {
1441   int element;
1442   int push_delay_fixed, push_delay_random;
1443 }
1444 push_delay_list[] =
1445 {
1446   { EL_SPRING,                  0, 0 },
1447   { EL_BALLOON,                 0, 0 },
1448
1449   { EL_SOKOBAN_OBJECT,          2, 0 },
1450   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1451   { EL_SATELLITE,               2, 0 },
1452   { EL_SP_DISK_YELLOW,          2, 0 },
1453
1454   { EL_UNDEFINED,               0, 0 },
1455 };
1456
1457 struct
1458 {
1459   int element;
1460   int move_stepsize;
1461 }
1462 move_stepsize_list[] =
1463 {
1464   { EL_AMOEBA_DROP,             2 },
1465   { EL_AMOEBA_DROPPING,         2 },
1466   { EL_QUICKSAND_FILLING,       1 },
1467   { EL_QUICKSAND_EMPTYING,      1 },
1468   { EL_QUICKSAND_FAST_FILLING,  2 },
1469   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1470   { EL_MAGIC_WALL_FILLING,      2 },
1471   { EL_MAGIC_WALL_EMPTYING,     2 },
1472   { EL_BD_MAGIC_WALL_FILLING,   2 },
1473   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1474   { EL_DC_MAGIC_WALL_FILLING,   2 },
1475   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1476
1477   { EL_UNDEFINED,               0 },
1478 };
1479
1480 struct
1481 {
1482   int element;
1483   int count;
1484 }
1485 collect_count_list[] =
1486 {
1487   { EL_EMERALD,                 1 },
1488   { EL_BD_DIAMOND,              1 },
1489   { EL_EMERALD_YELLOW,          1 },
1490   { EL_EMERALD_RED,             1 },
1491   { EL_EMERALD_PURPLE,          1 },
1492   { EL_DIAMOND,                 3 },
1493   { EL_SP_INFOTRON,             1 },
1494   { EL_PEARL,                   5 },
1495   { EL_CRYSTAL,                 8 },
1496
1497   { EL_UNDEFINED,               0 },
1498 };
1499
1500 struct
1501 {
1502   int element;
1503   int direction;
1504 }
1505 access_direction_list[] =
1506 {
1507   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1510   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1511   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1512   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1513   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1514   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1515   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1516   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1517   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1518
1519   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1520   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1521   { EL_SP_PORT_UP,                                                   MV_DOWN },
1522   { EL_SP_PORT_DOWN,                                         MV_UP           },
1523   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1524   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1525   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1527   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1528   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1529   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1530   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1531   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1532   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1533   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1534   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1535   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1536   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1537   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1538
1539   { EL_UNDEFINED,                       MV_NONE                              }
1540 };
1541
1542 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1543
1544 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1545 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1546 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1547                                  IS_JUST_CHANGING(x, y))
1548
1549 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1550
1551 /* static variables for playfield scan mode (scanning forward or backward) */
1552 static int playfield_scan_start_x = 0;
1553 static int playfield_scan_start_y = 0;
1554 static int playfield_scan_delta_x = 1;
1555 static int playfield_scan_delta_y = 1;
1556
1557 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1558                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1559                                      (y) += playfield_scan_delta_y)     \
1560                                 for ((x) = playfield_scan_start_x;      \
1561                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1562                                      (x) += playfield_scan_delta_x)
1563
1564 #ifdef DEBUG
1565 void DEBUG_SetMaximumDynamite()
1566 {
1567   int i;
1568
1569   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1570     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1571       local_player->inventory_element[local_player->inventory_size++] =
1572         EL_DYNAMITE;
1573 }
1574 #endif
1575
1576 static void InitPlayfieldScanModeVars()
1577 {
1578   if (game.use_reverse_scan_direction)
1579   {
1580     playfield_scan_start_x = lev_fieldx - 1;
1581     playfield_scan_start_y = lev_fieldy - 1;
1582
1583     playfield_scan_delta_x = -1;
1584     playfield_scan_delta_y = -1;
1585   }
1586   else
1587   {
1588     playfield_scan_start_x = 0;
1589     playfield_scan_start_y = 0;
1590
1591     playfield_scan_delta_x = 1;
1592     playfield_scan_delta_y = 1;
1593   }
1594 }
1595
1596 static void InitPlayfieldScanMode(int mode)
1597 {
1598   game.use_reverse_scan_direction =
1599     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1600
1601   InitPlayfieldScanModeVars();
1602 }
1603
1604 static int get_move_delay_from_stepsize(int move_stepsize)
1605 {
1606   move_stepsize =
1607     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1608
1609   /* make sure that stepsize value is always a power of 2 */
1610   move_stepsize = (1 << log_2(move_stepsize));
1611
1612   return TILEX / move_stepsize;
1613 }
1614
1615 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1616                                boolean init_game)
1617 {
1618   int player_nr = player->index_nr;
1619   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1620   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1621
1622   /* do no immediately change move delay -- the player might just be moving */
1623   player->move_delay_value_next = move_delay;
1624
1625   /* information if player can move must be set separately */
1626   player->cannot_move = cannot_move;
1627
1628   if (init_game)
1629   {
1630     player->move_delay       = game.initial_move_delay[player_nr];
1631     player->move_delay_value = game.initial_move_delay_value[player_nr];
1632
1633     player->move_delay_value_next = -1;
1634
1635     player->move_delay_reset_counter = 0;
1636   }
1637 }
1638
1639 void GetPlayerConfig()
1640 {
1641   GameFrameDelay = setup.game_frame_delay;
1642
1643   if (!audio.sound_available)
1644     setup.sound_simple = FALSE;
1645
1646   if (!audio.loops_available)
1647     setup.sound_loops = FALSE;
1648
1649   if (!audio.music_available)
1650     setup.sound_music = FALSE;
1651
1652   if (!video.fullscreen_available)
1653     setup.fullscreen = FALSE;
1654
1655   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1656
1657   SetAudioMode(setup.sound);
1658 }
1659
1660 int GetElementFromGroupElement(int element)
1661 {
1662   if (IS_GROUP_ELEMENT(element))
1663   {
1664     struct ElementGroupInfo *group = element_info[element].group;
1665     int last_anim_random_frame = gfx.anim_random_frame;
1666     int element_pos;
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = RND(group->num_elements_resolved);
1670
1671     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1672                                     group->choice_mode, 0,
1673                                     group->choice_pos);
1674
1675     if (group->choice_mode == ANIM_RANDOM)
1676       gfx.anim_random_frame = last_anim_random_frame;
1677
1678     group->choice_pos++;
1679
1680     element = group->element_resolved[element_pos];
1681   }
1682
1683   return element;
1684 }
1685
1686 static void InitPlayerField(int x, int y, int element, boolean init_game)
1687 {
1688   if (element == EL_SP_MURPHY)
1689   {
1690     if (init_game)
1691     {
1692       if (stored_player[0].present)
1693       {
1694         Feld[x][y] = EL_SP_MURPHY_CLONE;
1695
1696         return;
1697       }
1698       else
1699       {
1700         stored_player[0].initial_element = element;
1701         stored_player[0].use_murphy = TRUE;
1702
1703         if (!level.use_artwork_element[0])
1704           stored_player[0].artwork_element = EL_SP_MURPHY;
1705       }
1706
1707       Feld[x][y] = EL_PLAYER_1;
1708     }
1709   }
1710
1711   if (init_game)
1712   {
1713     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1714     int jx = player->jx, jy = player->jy;
1715
1716     player->present = TRUE;
1717
1718     player->block_last_field = (element == EL_SP_MURPHY ?
1719                                 level.sp_block_last_field :
1720                                 level.block_last_field);
1721
1722     /* ---------- initialize player's last field block delay --------------- */
1723
1724     /* always start with reliable default value (no adjustment needed) */
1725     player->block_delay_adjustment = 0;
1726
1727     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1728     if (player->block_last_field && element == EL_SP_MURPHY)
1729       player->block_delay_adjustment = 1;
1730
1731     /* special case 2: in game engines before 3.1.1, blocking was different */
1732     if (game.use_block_last_field_bug)
1733       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1734
1735     if (!network.enabled || player->connected_network)
1736     {
1737       player->active = TRUE;
1738
1739       /* remove potentially duplicate players */
1740       if (StorePlayer[jx][jy] == Feld[x][y])
1741         StorePlayer[jx][jy] = 0;
1742
1743       StorePlayer[x][y] = Feld[x][y];
1744
1745 #if DEBUG_INIT_PLAYER
1746       if (options.debug)
1747       {
1748         printf("- player element %d activated", player->element_nr);
1749         printf(" (local player is %d and currently %s)\n",
1750                local_player->element_nr,
1751                local_player->active ? "active" : "not active");
1752       }
1753     }
1754 #endif
1755
1756     Feld[x][y] = EL_EMPTY;
1757
1758     player->jx = player->last_jx = x;
1759     player->jy = player->last_jy = y;
1760   }
1761
1762   if (!init_game)
1763   {
1764     int player_nr = GET_PLAYER_NR(element);
1765     struct PlayerInfo *player = &stored_player[player_nr];
1766
1767     if (player->active && player->killed)
1768       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1769   }
1770 }
1771
1772 static void InitField(int x, int y, boolean init_game)
1773 {
1774   int element = Feld[x][y];
1775
1776   switch (element)
1777   {
1778     case EL_SP_MURPHY:
1779     case EL_PLAYER_1:
1780     case EL_PLAYER_2:
1781     case EL_PLAYER_3:
1782     case EL_PLAYER_4:
1783       InitPlayerField(x, y, element, init_game);
1784       break;
1785
1786     case EL_SOKOBAN_FIELD_PLAYER:
1787       element = Feld[x][y] = EL_PLAYER_1;
1788       InitField(x, y, init_game);
1789
1790       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1791       InitField(x, y, init_game);
1792       break;
1793
1794     case EL_SOKOBAN_FIELD_EMPTY:
1795       local_player->sokobanfields_still_needed++;
1796       break;
1797
1798     case EL_STONEBLOCK:
1799       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1800         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1801       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1802         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1804         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1806         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1807       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1808         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1809       break;
1810
1811     case EL_BUG:
1812     case EL_BUG_RIGHT:
1813     case EL_BUG_UP:
1814     case EL_BUG_LEFT:
1815     case EL_BUG_DOWN:
1816     case EL_SPACESHIP:
1817     case EL_SPACESHIP_RIGHT:
1818     case EL_SPACESHIP_UP:
1819     case EL_SPACESHIP_LEFT:
1820     case EL_SPACESHIP_DOWN:
1821     case EL_BD_BUTTERFLY:
1822     case EL_BD_BUTTERFLY_RIGHT:
1823     case EL_BD_BUTTERFLY_UP:
1824     case EL_BD_BUTTERFLY_LEFT:
1825     case EL_BD_BUTTERFLY_DOWN:
1826     case EL_BD_FIREFLY:
1827     case EL_BD_FIREFLY_RIGHT:
1828     case EL_BD_FIREFLY_UP:
1829     case EL_BD_FIREFLY_LEFT:
1830     case EL_BD_FIREFLY_DOWN:
1831     case EL_PACMAN_RIGHT:
1832     case EL_PACMAN_UP:
1833     case EL_PACMAN_LEFT:
1834     case EL_PACMAN_DOWN:
1835     case EL_YAMYAM:
1836     case EL_YAMYAM_LEFT:
1837     case EL_YAMYAM_RIGHT:
1838     case EL_YAMYAM_UP:
1839     case EL_YAMYAM_DOWN:
1840     case EL_DARK_YAMYAM:
1841     case EL_ROBOT:
1842     case EL_PACMAN:
1843     case EL_SP_SNIKSNAK:
1844     case EL_SP_ELECTRON:
1845     case EL_MOLE:
1846     case EL_MOLE_LEFT:
1847     case EL_MOLE_RIGHT:
1848     case EL_MOLE_UP:
1849     case EL_MOLE_DOWN:
1850       InitMovDir(x, y);
1851       break;
1852
1853     case EL_AMOEBA_FULL:
1854     case EL_BD_AMOEBA:
1855       InitAmoebaNr(x, y);
1856       break;
1857
1858     case EL_AMOEBA_DROP:
1859       if (y == lev_fieldy - 1)
1860       {
1861         Feld[x][y] = EL_AMOEBA_GROWING;
1862         Store[x][y] = EL_AMOEBA_WET;
1863       }
1864       break;
1865
1866     case EL_DYNAMITE_ACTIVE:
1867     case EL_SP_DISK_RED_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1870     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1871     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1872       MovDelay[x][y] = 96;
1873       break;
1874
1875     case EL_EM_DYNAMITE_ACTIVE:
1876       MovDelay[x][y] = 32;
1877       break;
1878
1879     case EL_LAMP:
1880       local_player->lights_still_needed++;
1881       break;
1882
1883     case EL_PENGUIN:
1884       local_player->friends_still_needed++;
1885       break;
1886
1887     case EL_PIG:
1888     case EL_DRAGON:
1889       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1890       break;
1891
1892     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1893     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1894     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1895     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1896     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1897     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1898     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1899     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1900     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1901     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1902     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1903     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1904       if (init_game)
1905       {
1906         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1907         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1908         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1909
1910         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1911         {
1912           game.belt_dir[belt_nr] = belt_dir;
1913           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1914         }
1915         else    /* more than one switch -- set it like the first switch */
1916         {
1917           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1918         }
1919       }
1920       break;
1921
1922     case EL_LIGHT_SWITCH_ACTIVE:
1923       if (init_game)
1924         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1925       break;
1926
1927     case EL_INVISIBLE_STEELWALL:
1928     case EL_INVISIBLE_WALL:
1929     case EL_INVISIBLE_SAND:
1930       if (game.light_time_left > 0 ||
1931           game.lenses_time_left > 0)
1932         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1933       break;
1934
1935     case EL_EMC_MAGIC_BALL:
1936       if (game.ball_state)
1937         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1938       break;
1939
1940     case EL_EMC_MAGIC_BALL_SWITCH:
1941       if (game.ball_state)
1942         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1943       break;
1944
1945     case EL_TRIGGER_PLAYER:
1946     case EL_TRIGGER_ELEMENT:
1947     case EL_TRIGGER_CE_VALUE:
1948     case EL_TRIGGER_CE_SCORE:
1949     case EL_SELF:
1950     case EL_ANY_ELEMENT:
1951     case EL_CURRENT_CE_VALUE:
1952     case EL_CURRENT_CE_SCORE:
1953     case EL_PREV_CE_1:
1954     case EL_PREV_CE_2:
1955     case EL_PREV_CE_3:
1956     case EL_PREV_CE_4:
1957     case EL_PREV_CE_5:
1958     case EL_PREV_CE_6:
1959     case EL_PREV_CE_7:
1960     case EL_PREV_CE_8:
1961     case EL_NEXT_CE_1:
1962     case EL_NEXT_CE_2:
1963     case EL_NEXT_CE_3:
1964     case EL_NEXT_CE_4:
1965     case EL_NEXT_CE_5:
1966     case EL_NEXT_CE_6:
1967     case EL_NEXT_CE_7:
1968     case EL_NEXT_CE_8:
1969       /* reference elements should not be used on the playfield */
1970       Feld[x][y] = EL_EMPTY;
1971       break;
1972
1973     default:
1974       if (IS_CUSTOM_ELEMENT(element))
1975       {
1976         if (CAN_MOVE(element))
1977           InitMovDir(x, y);
1978
1979         if (!element_info[element].use_last_ce_value || init_game)
1980           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1981       }
1982       else if (IS_GROUP_ELEMENT(element))
1983       {
1984         Feld[x][y] = GetElementFromGroupElement(element);
1985
1986         InitField(x, y, init_game);
1987       }
1988
1989       break;
1990   }
1991
1992   if (!init_game)
1993     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1994 }
1995
1996 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1997 {
1998   InitField(x, y, init_game);
1999
2000   /* not needed to call InitMovDir() -- already done by InitField()! */
2001   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2002       CAN_MOVE(Feld[x][y]))
2003     InitMovDir(x, y);
2004 }
2005
2006 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2007 {
2008   int old_element = Feld[x][y];
2009
2010   InitField(x, y, init_game);
2011
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(old_element) &&
2015       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2016     InitMovDir(x, y);
2017
2018   /* this case is in fact a combination of not less than three bugs:
2019      first, it calls InitMovDir() for elements that can move, although this is
2020      already done by InitField(); then, it checks the element that was at this
2021      field _before_ the call to InitField() (which can change it); lastly, it
2022      was not called for "mole with direction" elements, which were treated as
2023      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2024   */
2025 }
2026
2027 static int get_key_element_from_nr(int key_nr)
2028 {
2029   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2030                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2031                           EL_EM_KEY_1 : EL_KEY_1);
2032
2033   return key_base_element + key_nr;
2034 }
2035
2036 static int get_next_dropped_element(struct PlayerInfo *player)
2037 {
2038   return (player->inventory_size > 0 ?
2039           player->inventory_element[player->inventory_size - 1] :
2040           player->inventory_infinite_element != EL_UNDEFINED ?
2041           player->inventory_infinite_element :
2042           player->dynabombs_left > 0 ?
2043           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2044           EL_UNDEFINED);
2045 }
2046
2047 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2048 {
2049   /* pos >= 0: get element from bottom of the stack;
2050      pos <  0: get element from top of the stack */
2051
2052   if (pos < 0)
2053   {
2054     int min_inventory_size = -pos;
2055     int inventory_pos = player->inventory_size - min_inventory_size;
2056     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2057
2058     return (player->inventory_size >= min_inventory_size ?
2059             player->inventory_element[inventory_pos] :
2060             player->inventory_infinite_element != EL_UNDEFINED ?
2061             player->inventory_infinite_element :
2062             player->dynabombs_left >= min_dynabombs_left ?
2063             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2064             EL_UNDEFINED);
2065   }
2066   else
2067   {
2068     int min_dynabombs_left = pos + 1;
2069     int min_inventory_size = pos + 1 - player->dynabombs_left;
2070     int inventory_pos = pos - player->dynabombs_left;
2071
2072     return (player->inventory_infinite_element != EL_UNDEFINED ?
2073             player->inventory_infinite_element :
2074             player->dynabombs_left >= min_dynabombs_left ?
2075             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076             player->inventory_size >= min_inventory_size ?
2077             player->inventory_element[inventory_pos] :
2078             EL_UNDEFINED);
2079   }
2080 }
2081
2082 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2083 {
2084   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2085   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2086   int compare_result;
2087
2088   if (gpo1->sort_priority != gpo2->sort_priority)
2089     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2090   else
2091     compare_result = gpo1->nr - gpo2->nr;
2092
2093   return compare_result;
2094 }
2095
2096 int getPlayerInventorySize(int player_nr)
2097 {
2098   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2099     return level.native_em_level->ply[player_nr]->dynamite;
2100   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2101     return level.native_sp_level->game_sp->red_disk_count;
2102   else
2103     return stored_player[player_nr].inventory_size;
2104 }
2105
2106 void InitGameControlValues()
2107 {
2108   int i;
2109
2110   for (i = 0; game_panel_controls[i].nr != -1; i++)
2111   {
2112     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2113     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2114     struct TextPosInfo *pos = gpc->pos;
2115     int nr = gpc->nr;
2116     int type = gpc->type;
2117
2118     if (nr != i)
2119     {
2120       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2121       Error(ERR_EXIT, "this should not happen -- please debug");
2122     }
2123
2124     /* force update of game controls after initialization */
2125     gpc->value = gpc->last_value = -1;
2126     gpc->frame = gpc->last_frame = -1;
2127     gpc->gfx_frame = -1;
2128
2129     /* determine panel value width for later calculation of alignment */
2130     if (type == TYPE_INTEGER || type == TYPE_STRING)
2131     {
2132       pos->width = pos->size * getFontWidth(pos->font);
2133       pos->height = getFontHeight(pos->font);
2134     }
2135     else if (type == TYPE_ELEMENT)
2136     {
2137       pos->width = pos->size;
2138       pos->height = pos->size;
2139     }
2140
2141     /* fill structure for game panel draw order */
2142     gpo->nr = gpc->nr;
2143     gpo->sort_priority = pos->sort_priority;
2144   }
2145
2146   /* sort game panel controls according to sort_priority and control number */
2147   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2148         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2149 }
2150
2151 void UpdatePlayfieldElementCount()
2152 {
2153   boolean use_element_count = FALSE;
2154   int i, j, x, y;
2155
2156   /* first check if it is needed at all to calculate playfield element count */
2157   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2158     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2159       use_element_count = TRUE;
2160
2161   if (!use_element_count)
2162     return;
2163
2164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2165     element_info[i].element_count = 0;
2166
2167   SCAN_PLAYFIELD(x, y)
2168   {
2169     element_info[Feld[x][y]].element_count++;
2170   }
2171
2172   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2173     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2174       if (IS_IN_GROUP(j, i))
2175         element_info[EL_GROUP_START + i].element_count +=
2176           element_info[j].element_count;
2177 }
2178
2179 void UpdateGameControlValues()
2180 {
2181   int i, k;
2182   int time = (local_player->LevelSolved ?
2183               local_player->LevelSolved_CountingTime :
2184               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2185               level.native_em_level->lev->time :
2186               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2187               level.native_sp_level->game_sp->time_played :
2188               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2189               game_mm.energy_left :
2190               game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192                local_player->LevelSolved_CountingScore :
2193                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194                level.native_em_level->lev->score :
2195                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196                level.native_sp_level->game_sp->score :
2197                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198                game_mm.score :
2199                local_player->score);
2200   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201               level.native_em_level->lev->required :
2202               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203               level.native_sp_level->game_sp->infotrons_still_needed :
2204               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205               game_mm.kettles_still_needed :
2206               local_player->gems_still_needed);
2207   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2208                      level.native_em_level->lev->required > 0 :
2209                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2210                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2211                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2212                      game_mm.kettles_still_needed > 0 ||
2213                      game_mm.lights_still_needed > 0 :
2214                      local_player->gems_still_needed > 0 ||
2215                      local_player->sokobanfields_still_needed > 0 ||
2216                      local_player->lights_still_needed > 0);
2217   int health = (local_player->LevelSolved ?
2218                 local_player->LevelSolved_CountingHealth :
2219                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2220                 MM_HEALTH(game_mm.laser_overload_value) :
2221                 local_player->health);
2222
2223   UpdatePlayfieldElementCount();
2224
2225   /* update game panel control values */
2226
2227   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2228   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2229
2230   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2231   for (i = 0; i < MAX_NUM_KEYS; i++)
2232     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2233   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2234   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2235
2236   if (game.centered_player_nr == -1)
2237   {
2238     for (i = 0; i < MAX_PLAYERS; i++)
2239     {
2240       /* only one player in Supaplex game engine */
2241       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2242         break;
2243
2244       for (k = 0; k < MAX_NUM_KEYS; k++)
2245       {
2246         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2247         {
2248           if (level.native_em_level->ply[i]->keys & (1 << k))
2249             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2250               get_key_element_from_nr(k);
2251         }
2252         else if (stored_player[i].key[k])
2253           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2254             get_key_element_from_nr(k);
2255       }
2256
2257       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2258         getPlayerInventorySize(i);
2259
2260       if (stored_player[i].num_white_keys > 0)
2261         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2262           EL_DC_KEY_WHITE;
2263
2264       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2265         stored_player[i].num_white_keys;
2266     }
2267   }
2268   else
2269   {
2270     int player_nr = game.centered_player_nr;
2271
2272     for (k = 0; k < MAX_NUM_KEYS; k++)
2273     {
2274       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2275       {
2276         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2277           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2278             get_key_element_from_nr(k);
2279       }
2280       else if (stored_player[player_nr].key[k])
2281         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2282           get_key_element_from_nr(k);
2283     }
2284
2285     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2286       getPlayerInventorySize(player_nr);
2287
2288     if (stored_player[player_nr].num_white_keys > 0)
2289       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2290
2291     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2292       stored_player[player_nr].num_white_keys;
2293   }
2294
2295   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2296   {
2297     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2298       get_inventory_element_from_pos(local_player, i);
2299     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2300       get_inventory_element_from_pos(local_player, -i - 1);
2301   }
2302
2303   game_panel_controls[GAME_PANEL_SCORE].value = score;
2304   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2305
2306   game_panel_controls[GAME_PANEL_TIME].value = time;
2307
2308   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2309   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2310   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2311
2312   if (level.time == 0)
2313     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2314   else
2315     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2316
2317   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2318   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2319
2320   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2321
2322   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2323     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2324      EL_EMPTY);
2325   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2326     local_player->shield_normal_time_left;
2327   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2328     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2329      EL_EMPTY);
2330   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2331     local_player->shield_deadly_time_left;
2332
2333   game_panel_controls[GAME_PANEL_EXIT].value =
2334     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2335
2336   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2337     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2338   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2339     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2340      EL_EMC_MAGIC_BALL_SWITCH);
2341
2342   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2343     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2344   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2345     game.light_time_left;
2346
2347   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2348     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2349   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2350     game.timegate_time_left;
2351
2352   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2353     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2354
2355   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2356     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2357   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2358     game.lenses_time_left;
2359
2360   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2361     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2362   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2363     game.magnify_time_left;
2364
2365   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2366     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2367      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2368      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2369      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2370      EL_BALLOON_SWITCH_NONE);
2371
2372   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2373     local_player->dynabomb_count;
2374   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2375     local_player->dynabomb_size;
2376   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2377     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2378
2379   game_panel_controls[GAME_PANEL_PENGUINS].value =
2380     local_player->friends_still_needed;
2381
2382   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2383     local_player->sokobanfields_still_needed;
2384   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2385     local_player->sokobanfields_still_needed;
2386
2387   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2388     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2389
2390   for (i = 0; i < NUM_BELTS; i++)
2391   {
2392     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2393       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2394        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2395     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2396       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2397   }
2398
2399   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2400     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2401   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2402     game.magic_wall_time_left;
2403
2404   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2405     local_player->gravity;
2406
2407   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2408     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2409
2410   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2411     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2412       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2413        game.panel.element[i].id : EL_UNDEFINED);
2414
2415   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2416     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2417       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2418        element_info[game.panel.element_count[i].id].element_count : 0);
2419
2420   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2421     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2422       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2423        element_info[game.panel.ce_score[i].id].collect_score : 0);
2424
2425   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2426     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2427       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2428        element_info[game.panel.ce_score_element[i].id].collect_score :
2429        EL_UNDEFINED);
2430
2431   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2432   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2433   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2434
2435   /* update game panel control frames */
2436
2437   for (i = 0; game_panel_controls[i].nr != -1; i++)
2438   {
2439     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2440
2441     if (gpc->type == TYPE_ELEMENT)
2442     {
2443       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2444       {
2445         int last_anim_random_frame = gfx.anim_random_frame;
2446         int element = gpc->value;
2447         int graphic = el2panelimg(element);
2448
2449         if (gpc->value != gpc->last_value)
2450         {
2451           gpc->gfx_frame = 0;
2452           gpc->gfx_random = INIT_GFX_RANDOM();
2453         }
2454         else
2455         {
2456           gpc->gfx_frame++;
2457
2458           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2459               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2460             gpc->gfx_random = INIT_GFX_RANDOM();
2461         }
2462
2463         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2464           gfx.anim_random_frame = gpc->gfx_random;
2465
2466         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2467           gpc->gfx_frame = element_info[element].collect_score;
2468
2469         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2470                                               gpc->gfx_frame);
2471
2472         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2473           gfx.anim_random_frame = last_anim_random_frame;
2474       }
2475     }
2476     else if (gpc->type == TYPE_GRAPHIC)
2477     {
2478       if (gpc->graphic != IMG_UNDEFINED)
2479       {
2480         int last_anim_random_frame = gfx.anim_random_frame;
2481         int graphic = gpc->graphic;
2482
2483         if (gpc->value != gpc->last_value)
2484         {
2485           gpc->gfx_frame = 0;
2486           gpc->gfx_random = INIT_GFX_RANDOM();
2487         }
2488         else
2489         {
2490           gpc->gfx_frame++;
2491
2492           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2493               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2494             gpc->gfx_random = INIT_GFX_RANDOM();
2495         }
2496
2497         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2498           gfx.anim_random_frame = gpc->gfx_random;
2499
2500         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2501
2502         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2503           gfx.anim_random_frame = last_anim_random_frame;
2504       }
2505     }
2506   }
2507 }
2508
2509 void DisplayGameControlValues()
2510 {
2511   boolean redraw_panel = FALSE;
2512   int i;
2513
2514   for (i = 0; game_panel_controls[i].nr != -1; i++)
2515   {
2516     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2517
2518     if (PANEL_DEACTIVATED(gpc->pos))
2519       continue;
2520
2521     if (gpc->value == gpc->last_value &&
2522         gpc->frame == gpc->last_frame)
2523       continue;
2524
2525     redraw_panel = TRUE;
2526   }
2527
2528   if (!redraw_panel)
2529     return;
2530
2531   /* copy default game door content to main double buffer */
2532
2533   /* !!! CHECK AGAIN !!! */
2534   SetPanelBackground();
2535   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2536   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2537
2538   /* redraw game control buttons */
2539   RedrawGameButtons();
2540
2541   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2542
2543   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2544   {
2545     int nr = game_panel_order[i].nr;
2546     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2547     struct TextPosInfo *pos = gpc->pos;
2548     int type = gpc->type;
2549     int value = gpc->value;
2550     int frame = gpc->frame;
2551     int size = pos->size;
2552     int font = pos->font;
2553     boolean draw_masked = pos->draw_masked;
2554     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555
2556     if (PANEL_DEACTIVATED(pos))
2557       continue;
2558
2559     gpc->last_value = value;
2560     gpc->last_frame = frame;
2561
2562     if (type == TYPE_INTEGER)
2563     {
2564       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2565           nr == GAME_PANEL_TIME)
2566       {
2567         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2568
2569         if (use_dynamic_size)           /* use dynamic number of digits */
2570         {
2571           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2572           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2573           int size2 = size1 + 1;
2574           int font1 = pos->font;
2575           int font2 = pos->font_alt;
2576
2577           size = (value < value_change ? size1 : size2);
2578           font = (value < value_change ? font1 : font2);
2579         }
2580       }
2581
2582       /* correct text size if "digits" is zero or less */
2583       if (size <= 0)
2584         size = strlen(int2str(value, size));
2585
2586       /* dynamically correct text alignment */
2587       pos->width = size * getFontWidth(font);
2588
2589       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2590                   int2str(value, size), font, mask_mode);
2591     }
2592     else if (type == TYPE_ELEMENT)
2593     {
2594       int element, graphic;
2595       Bitmap *src_bitmap;
2596       int src_x, src_y;
2597       int width, height;
2598       int dst_x = PANEL_XPOS(pos);
2599       int dst_y = PANEL_YPOS(pos);
2600
2601       if (value != EL_UNDEFINED && value != EL_EMPTY)
2602       {
2603         element = value;
2604         graphic = el2panelimg(value);
2605
2606         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2607
2608         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2609           size = TILESIZE;
2610
2611         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2612                               &src_x, &src_y);
2613
2614         width  = graphic_info[graphic].width  * size / TILESIZE;
2615         height = graphic_info[graphic].height * size / TILESIZE;
2616
2617         if (draw_masked)
2618           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2619                            dst_x, dst_y);
2620         else
2621           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2622                      dst_x, dst_y);
2623       }
2624     }
2625     else if (type == TYPE_GRAPHIC)
2626     {
2627       int graphic        = gpc->graphic;
2628       int graphic_active = gpc->graphic_active;
2629       Bitmap *src_bitmap;
2630       int src_x, src_y;
2631       int width, height;
2632       int dst_x = PANEL_XPOS(pos);
2633       int dst_y = PANEL_YPOS(pos);
2634       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2635                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2636
2637       if (graphic != IMG_UNDEFINED && !skip)
2638       {
2639         if (pos->style == STYLE_REVERSE)
2640           value = 100 - value;
2641
2642         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2643
2644         if (pos->direction & MV_HORIZONTAL)
2645         {
2646           width  = graphic_info[graphic_active].width * value / 100;
2647           height = graphic_info[graphic_active].height;
2648
2649           if (pos->direction == MV_LEFT)
2650           {
2651             src_x += graphic_info[graphic_active].width - width;
2652             dst_x += graphic_info[graphic_active].width - width;
2653           }
2654         }
2655         else
2656         {
2657           width  = graphic_info[graphic_active].width;
2658           height = graphic_info[graphic_active].height * value / 100;
2659
2660           if (pos->direction == MV_UP)
2661           {
2662             src_y += graphic_info[graphic_active].height - height;
2663             dst_y += graphic_info[graphic_active].height - height;
2664           }
2665         }
2666
2667         if (draw_masked)
2668           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2669                            dst_x, dst_y);
2670         else
2671           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2672                      dst_x, dst_y);
2673
2674         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2675
2676         if (pos->direction & MV_HORIZONTAL)
2677         {
2678           if (pos->direction == MV_RIGHT)
2679           {
2680             src_x += width;
2681             dst_x += width;
2682           }
2683           else
2684           {
2685             dst_x = PANEL_XPOS(pos);
2686           }
2687
2688           width = graphic_info[graphic].width - width;
2689         }
2690         else
2691         {
2692           if (pos->direction == MV_DOWN)
2693           {
2694             src_y += height;
2695             dst_y += height;
2696           }
2697           else
2698           {
2699             dst_y = PANEL_YPOS(pos);
2700           }
2701
2702           height = graphic_info[graphic].height - height;
2703         }
2704
2705         if (draw_masked)
2706           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2707                            dst_x, dst_y);
2708         else
2709           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2710                      dst_x, dst_y);
2711       }
2712     }
2713     else if (type == TYPE_STRING)
2714     {
2715       boolean active = (value != 0);
2716       char *state_normal = "off";
2717       char *state_active = "on";
2718       char *state = (active ? state_active : state_normal);
2719       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2720                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2721                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2722                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2723
2724       if (nr == GAME_PANEL_GRAVITY_STATE)
2725       {
2726         int font1 = pos->font;          /* (used for normal state) */
2727         int font2 = pos->font_alt;      /* (used for active state) */
2728
2729         font = (active ? font2 : font1);
2730       }
2731
2732       if (s != NULL)
2733       {
2734         char *s_cut;
2735
2736         if (size <= 0)
2737         {
2738           /* don't truncate output if "chars" is zero or less */
2739           size = strlen(s);
2740
2741           /* dynamically correct text alignment */
2742           pos->width = size * getFontWidth(font);
2743         }
2744
2745         s_cut = getStringCopyN(s, size);
2746
2747         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2748                     s_cut, font, mask_mode);
2749
2750         free(s_cut);
2751       }
2752     }
2753
2754     redraw_mask |= REDRAW_DOOR_1;
2755   }
2756
2757   SetGameStatus(GAME_MODE_PLAYING);
2758 }
2759
2760 void UpdateAndDisplayGameControlValues()
2761 {
2762   if (tape.deactivate_display)
2763     return;
2764
2765   UpdateGameControlValues();
2766   DisplayGameControlValues();
2767 }
2768
2769 void UpdateGameDoorValues()
2770 {
2771   UpdateGameControlValues();
2772 }
2773
2774 void DrawGameDoorValues()
2775 {
2776   DisplayGameControlValues();
2777 }
2778
2779
2780 /*
2781   =============================================================================
2782   InitGameEngine()
2783   -----------------------------------------------------------------------------
2784   initialize game engine due to level / tape version number
2785   =============================================================================
2786 */
2787
2788 static void InitGameEngine()
2789 {
2790   int i, j, k, l, x, y;
2791
2792   /* set game engine from tape file when re-playing, else from level file */
2793   game.engine_version = (tape.playing ? tape.engine_version :
2794                          level.game_version);
2795
2796   /* set single or multi-player game mode (needed for re-playing tapes) */
2797   game.team_mode = setup.team_mode;
2798
2799   if (tape.playing)
2800   {
2801     int num_players = 0;
2802
2803     for (i = 0; i < MAX_PLAYERS; i++)
2804       if (tape.player_participates[i])
2805         num_players++;
2806
2807     /* multi-player tapes contain input data for more than one player */
2808     game.team_mode = (num_players > 1);
2809   }
2810
2811   /* ---------------------------------------------------------------------- */
2812   /* set flags for bugs and changes according to active game engine version */
2813   /* ---------------------------------------------------------------------- */
2814
2815   /*
2816     Summary of bugfix/change:
2817     Fixed handling for custom elements that change when pushed by the player.
2818
2819     Fixed/changed in version:
2820     3.1.0
2821
2822     Description:
2823     Before 3.1.0, custom elements that "change when pushing" changed directly
2824     after the player started pushing them (until then handled in "DigField()").
2825     Since 3.1.0, these custom elements are not changed until the "pushing"
2826     move of the element is finished (now handled in "ContinueMoving()").
2827
2828     Affected levels/tapes:
2829     The first condition is generally needed for all levels/tapes before version
2830     3.1.0, which might use the old behaviour before it was changed; known tapes
2831     that are affected are some tapes from the level set "Walpurgis Gardens" by
2832     Jamie Cullen.
2833     The second condition is an exception from the above case and is needed for
2834     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2835     above (including some development versions of 3.1.0), but before it was
2836     known that this change would break tapes like the above and was fixed in
2837     3.1.1, so that the changed behaviour was active although the engine version
2838     while recording maybe was before 3.1.0. There is at least one tape that is
2839     affected by this exception, which is the tape for the one-level set "Bug
2840     Machine" by Juergen Bonhagen.
2841   */
2842
2843   game.use_change_when_pushing_bug =
2844     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2845      !(tape.playing &&
2846        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2847        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2848
2849   /*
2850     Summary of bugfix/change:
2851     Fixed handling for blocking the field the player leaves when moving.
2852
2853     Fixed/changed in version:
2854     3.1.1
2855
2856     Description:
2857     Before 3.1.1, when "block last field when moving" was enabled, the field
2858     the player is leaving when moving was blocked for the time of the move,
2859     and was directly unblocked afterwards. This resulted in the last field
2860     being blocked for exactly one less than the number of frames of one player
2861     move. Additionally, even when blocking was disabled, the last field was
2862     blocked for exactly one frame.
2863     Since 3.1.1, due to changes in player movement handling, the last field
2864     is not blocked at all when blocking is disabled. When blocking is enabled,
2865     the last field is blocked for exactly the number of frames of one player
2866     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2867     last field is blocked for exactly one more than the number of frames of
2868     one player move.
2869
2870     Affected levels/tapes:
2871     (!!! yet to be determined -- probably many !!!)
2872   */
2873
2874   game.use_block_last_field_bug =
2875     (game.engine_version < VERSION_IDENT(3,1,1,0));
2876
2877   game_em.use_single_button =
2878     (game.engine_version > VERSION_IDENT(4,0,0,2));
2879
2880   game_em.use_snap_key_bug =
2881     (game.engine_version < VERSION_IDENT(4,0,1,0));
2882
2883   /* ---------------------------------------------------------------------- */
2884
2885   /* set maximal allowed number of custom element changes per game frame */
2886   game.max_num_changes_per_frame = 1;
2887
2888   /* default scan direction: scan playfield from top/left to bottom/right */
2889   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2890
2891   /* dynamically adjust element properties according to game engine version */
2892   InitElementPropertiesEngine(game.engine_version);
2893
2894 #if 0
2895   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2896   printf("          tape version == %06d [%s] [file: %06d]\n",
2897          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2898          tape.file_version);
2899   printf("       => game.engine_version == %06d\n", game.engine_version);
2900 #endif
2901
2902   /* ---------- initialize player's initial move delay --------------------- */
2903
2904   /* dynamically adjust player properties according to level information */
2905   for (i = 0; i < MAX_PLAYERS; i++)
2906     game.initial_move_delay_value[i] =
2907       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2908
2909   /* dynamically adjust player properties according to game engine version */
2910   for (i = 0; i < MAX_PLAYERS; i++)
2911     game.initial_move_delay[i] =
2912       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2913        game.initial_move_delay_value[i] : 0);
2914
2915   /* ---------- initialize player's initial push delay --------------------- */
2916
2917   /* dynamically adjust player properties according to game engine version */
2918   game.initial_push_delay_value =
2919     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2920
2921   /* ---------- initialize changing elements ------------------------------- */
2922
2923   /* initialize changing elements information */
2924   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2925   {
2926     struct ElementInfo *ei = &element_info[i];
2927
2928     /* this pointer might have been changed in the level editor */
2929     ei->change = &ei->change_page[0];
2930
2931     if (!IS_CUSTOM_ELEMENT(i))
2932     {
2933       ei->change->target_element = EL_EMPTY_SPACE;
2934       ei->change->delay_fixed = 0;
2935       ei->change->delay_random = 0;
2936       ei->change->delay_frames = 1;
2937     }
2938
2939     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2940     {
2941       ei->has_change_event[j] = FALSE;
2942
2943       ei->event_page_nr[j] = 0;
2944       ei->event_page[j] = &ei->change_page[0];
2945     }
2946   }
2947
2948   /* add changing elements from pre-defined list */
2949   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2950   {
2951     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2952     struct ElementInfo *ei = &element_info[ch_delay->element];
2953
2954     ei->change->target_element       = ch_delay->target_element;
2955     ei->change->delay_fixed          = ch_delay->change_delay;
2956
2957     ei->change->pre_change_function  = ch_delay->pre_change_function;
2958     ei->change->change_function      = ch_delay->change_function;
2959     ei->change->post_change_function = ch_delay->post_change_function;
2960
2961     ei->change->can_change = TRUE;
2962     ei->change->can_change_or_has_action = TRUE;
2963
2964     ei->has_change_event[CE_DELAY] = TRUE;
2965
2966     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2967     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2968   }
2969
2970   /* ---------- initialize internal run-time variables --------------------- */
2971
2972   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2973   {
2974     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2975
2976     for (j = 0; j < ei->num_change_pages; j++)
2977     {
2978       ei->change_page[j].can_change_or_has_action =
2979         (ei->change_page[j].can_change |
2980          ei->change_page[j].has_action);
2981     }
2982   }
2983
2984   /* add change events from custom element configuration */
2985   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2986   {
2987     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2988
2989     for (j = 0; j < ei->num_change_pages; j++)
2990     {
2991       if (!ei->change_page[j].can_change_or_has_action)
2992         continue;
2993
2994       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2995       {
2996         /* only add event page for the first page found with this event */
2997         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2998         {
2999           ei->has_change_event[k] = TRUE;
3000
3001           ei->event_page_nr[k] = j;
3002           ei->event_page[k] = &ei->change_page[j];
3003         }
3004       }
3005     }
3006   }
3007
3008   /* ---------- initialize reference elements in change conditions --------- */
3009
3010   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3011   {
3012     int element = EL_CUSTOM_START + i;
3013     struct ElementInfo *ei = &element_info[element];
3014
3015     for (j = 0; j < ei->num_change_pages; j++)
3016     {
3017       int trigger_element = ei->change_page[j].initial_trigger_element;
3018
3019       if (trigger_element >= EL_PREV_CE_8 &&
3020           trigger_element <= EL_NEXT_CE_8)
3021         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3022
3023       ei->change_page[j].trigger_element = trigger_element;
3024     }
3025   }
3026
3027   /* ---------- initialize run-time trigger player and element ------------- */
3028
3029   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3030   {
3031     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3032
3033     for (j = 0; j < ei->num_change_pages; j++)
3034     {
3035       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3036       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3037       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3038       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3039       ei->change_page[j].actual_trigger_ce_value = 0;
3040       ei->change_page[j].actual_trigger_ce_score = 0;
3041     }
3042   }
3043
3044   /* ---------- initialize trigger events ---------------------------------- */
3045
3046   /* initialize trigger events information */
3047   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3048     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3049       trigger_events[i][j] = FALSE;
3050
3051   /* add trigger events from element change event properties */
3052   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3053   {
3054     struct ElementInfo *ei = &element_info[i];
3055
3056     for (j = 0; j < ei->num_change_pages; j++)
3057     {
3058       if (!ei->change_page[j].can_change_or_has_action)
3059         continue;
3060
3061       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3062       {
3063         int trigger_element = ei->change_page[j].trigger_element;
3064
3065         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3066         {
3067           if (ei->change_page[j].has_event[k])
3068           {
3069             if (IS_GROUP_ELEMENT(trigger_element))
3070             {
3071               struct ElementGroupInfo *group =
3072                 element_info[trigger_element].group;
3073
3074               for (l = 0; l < group->num_elements_resolved; l++)
3075                 trigger_events[group->element_resolved[l]][k] = TRUE;
3076             }
3077             else if (trigger_element == EL_ANY_ELEMENT)
3078               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3079                 trigger_events[l][k] = TRUE;
3080             else
3081               trigger_events[trigger_element][k] = TRUE;
3082           }
3083         }
3084       }
3085     }
3086   }
3087
3088   /* ---------- initialize push delay -------------------------------------- */
3089
3090   /* initialize push delay values to default */
3091   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3092   {
3093     if (!IS_CUSTOM_ELEMENT(i))
3094     {
3095       /* set default push delay values (corrected since version 3.0.7-1) */
3096       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3097       {
3098         element_info[i].push_delay_fixed = 2;
3099         element_info[i].push_delay_random = 8;
3100       }
3101       else
3102       {
3103         element_info[i].push_delay_fixed = 8;
3104         element_info[i].push_delay_random = 8;
3105       }
3106     }
3107   }
3108
3109   /* set push delay value for certain elements from pre-defined list */
3110   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3111   {
3112     int e = push_delay_list[i].element;
3113
3114     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3115     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3116   }
3117
3118   /* set push delay value for Supaplex elements for newer engine versions */
3119   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3120   {
3121     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3122     {
3123       if (IS_SP_ELEMENT(i))
3124       {
3125         /* set SP push delay to just enough to push under a falling zonk */
3126         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3127
3128         element_info[i].push_delay_fixed  = delay;
3129         element_info[i].push_delay_random = 0;
3130       }
3131     }
3132   }
3133
3134   /* ---------- initialize move stepsize ----------------------------------- */
3135
3136   /* initialize move stepsize values to default */
3137   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3138     if (!IS_CUSTOM_ELEMENT(i))
3139       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3140
3141   /* set move stepsize value for certain elements from pre-defined list */
3142   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3143   {
3144     int e = move_stepsize_list[i].element;
3145
3146     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3147   }
3148
3149   /* ---------- initialize collect score ----------------------------------- */
3150
3151   /* initialize collect score values for custom elements from initial value */
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153     if (IS_CUSTOM_ELEMENT(i))
3154       element_info[i].collect_score = element_info[i].collect_score_initial;
3155
3156   /* ---------- initialize collect count ----------------------------------- */
3157
3158   /* initialize collect count values for non-custom elements */
3159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3160     if (!IS_CUSTOM_ELEMENT(i))
3161       element_info[i].collect_count_initial = 0;
3162
3163   /* add collect count values for all elements from pre-defined list */
3164   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3165     element_info[collect_count_list[i].element].collect_count_initial =
3166       collect_count_list[i].count;
3167
3168   /* ---------- initialize access direction -------------------------------- */
3169
3170   /* initialize access direction values to default (access from every side) */
3171   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3172     if (!IS_CUSTOM_ELEMENT(i))
3173       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3174
3175   /* set access direction value for certain elements from pre-defined list */
3176   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3177     element_info[access_direction_list[i].element].access_direction =
3178       access_direction_list[i].direction;
3179
3180   /* ---------- initialize explosion content ------------------------------- */
3181   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3182   {
3183     if (IS_CUSTOM_ELEMENT(i))
3184       continue;
3185
3186     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3187     {
3188       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3189
3190       element_info[i].content.e[x][y] =
3191         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3192          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3193          i == EL_PLAYER_3 ? EL_EMERALD :
3194          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3195          i == EL_MOLE ? EL_EMERALD_RED :
3196          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3197          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3198          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3199          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3200          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3201          i == EL_WALL_EMERALD ? EL_EMERALD :
3202          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3203          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3204          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3205          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3206          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3207          i == EL_WALL_PEARL ? EL_PEARL :
3208          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3209          EL_EMPTY);
3210     }
3211   }
3212
3213   /* ---------- initialize recursion detection ------------------------------ */
3214   recursion_loop_depth = 0;
3215   recursion_loop_detected = FALSE;
3216   recursion_loop_element = EL_UNDEFINED;
3217
3218   /* ---------- initialize graphics engine ---------------------------------- */
3219   game.scroll_delay_value =
3220     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3221      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3222   game.scroll_delay_value =
3223     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3224
3225   /* ---------- initialize game engine snapshots ---------------------------- */
3226   for (i = 0; i < MAX_PLAYERS; i++)
3227     game.snapshot.last_action[i] = 0;
3228   game.snapshot.changed_action = FALSE;
3229   game.snapshot.collected_item = FALSE;
3230   game.snapshot.mode =
3231     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3232      SNAPSHOT_MODE_EVERY_STEP :
3233      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3234      SNAPSHOT_MODE_EVERY_MOVE :
3235      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3236      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3237   game.snapshot.save_snapshot = FALSE;
3238
3239   /* ---------- initialize level time for Supaplex engine ------------------- */
3240   /* Supaplex levels with time limit currently unsupported -- should be added */
3241   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3242     level.time = 0;
3243 }
3244
3245 int get_num_special_action(int element, int action_first, int action_last)
3246 {
3247   int num_special_action = 0;
3248   int i, j;
3249
3250   for (i = action_first; i <= action_last; i++)
3251   {
3252     boolean found = FALSE;
3253
3254     for (j = 0; j < NUM_DIRECTIONS; j++)
3255       if (el_act_dir2img(element, i, j) !=
3256           el_act_dir2img(element, ACTION_DEFAULT, j))
3257         found = TRUE;
3258
3259     if (found)
3260       num_special_action++;
3261     else
3262       break;
3263   }
3264
3265   return num_special_action;
3266 }
3267
3268
3269 /*
3270   =============================================================================
3271   InitGame()
3272   -----------------------------------------------------------------------------
3273   initialize and start new game
3274   =============================================================================
3275 */
3276
3277 #if DEBUG_INIT_PLAYER
3278 static void DebugPrintPlayerStatus(char *message)
3279 {
3280   int i;
3281
3282   if (!options.debug)
3283     return;
3284
3285   printf("%s:\n", message);
3286
3287   for (i = 0; i < MAX_PLAYERS; i++)
3288   {
3289     struct PlayerInfo *player = &stored_player[i];
3290
3291     printf("- player %d: present == %d, connected == %d [%d/%d], active == %d",
3292            i + 1,
3293            player->present,
3294            player->connected,
3295            player->connected_locally,
3296            player->connected_network,
3297            player->active);
3298
3299     if (local_player == player)
3300       printf(" (local player)");
3301
3302     printf("\n");
3303   }
3304 }
3305 #endif
3306
3307 void InitGame()
3308 {
3309   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3310   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3311   int fade_mask = REDRAW_FIELD;
3312
3313   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3314   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3315   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3316   int initial_move_dir = MV_DOWN;
3317   int i, j, x, y;
3318
3319   // required here to update video display before fading (FIX THIS)
3320   DrawMaskedBorder(REDRAW_DOOR_2);
3321
3322   if (!game.restart_level)
3323     CloseDoor(DOOR_CLOSE_1);
3324
3325   SetGameStatus(GAME_MODE_PLAYING);
3326
3327   if (level_editor_test_game)
3328     FadeSkipNextFadeIn();
3329   else
3330     FadeSetEnterScreen();
3331
3332   if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged())
3333     fade_mask = REDRAW_ALL;
3334
3335   FadeLevelSoundsAndMusic();
3336
3337   ExpireSoundLoops(TRUE);
3338
3339   if (!level_editor_test_game)
3340     FadeOut(fade_mask);
3341
3342   /* needed if different viewport properties defined for playing */
3343   ChangeViewportPropertiesIfNeeded();
3344
3345   ClearField();
3346
3347   DrawCompleteVideoDisplay();
3348
3349   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3350
3351   InitGameEngine();
3352   InitGameControlValues();
3353
3354   /* don't play tapes over network */
3355   network_playing = (network.enabled && !tape.playing);
3356
3357   for (i = 0; i < MAX_PLAYERS; i++)
3358   {
3359     struct PlayerInfo *player = &stored_player[i];
3360
3361     player->index_nr = i;
3362     player->index_bit = (1 << i);
3363     player->element_nr = EL_PLAYER_1 + i;
3364
3365     player->present = FALSE;
3366     player->active = FALSE;
3367     player->mapped = FALSE;
3368
3369     player->killed = FALSE;
3370     player->reanimated = FALSE;
3371
3372     player->action = 0;
3373     player->effective_action = 0;
3374     player->programmed_action = 0;
3375
3376     player->mouse_action.lx = 0;
3377     player->mouse_action.ly = 0;
3378     player->mouse_action.button = 0;
3379     player->mouse_action.button_hint = 0;
3380
3381     player->effective_mouse_action.lx = 0;
3382     player->effective_mouse_action.ly = 0;
3383     player->effective_mouse_action.button = 0;
3384     player->effective_mouse_action.button_hint = 0;
3385
3386     player->score = 0;
3387     player->score_final = 0;
3388
3389     player->health = MAX_HEALTH;
3390     player->health_final = MAX_HEALTH;
3391
3392     player->gems_still_needed = level.gems_needed;
3393     player->sokobanfields_still_needed = 0;
3394     player->lights_still_needed = 0;
3395     player->friends_still_needed = 0;
3396
3397     for (j = 0; j < MAX_NUM_KEYS; j++)
3398       player->key[j] = FALSE;
3399
3400     player->num_white_keys = 0;
3401
3402     player->dynabomb_count = 0;
3403     player->dynabomb_size = 1;
3404     player->dynabombs_left = 0;
3405     player->dynabomb_xl = FALSE;
3406
3407     player->MovDir = initial_move_dir;
3408     player->MovPos = 0;
3409     player->GfxPos = 0;
3410     player->GfxDir = initial_move_dir;
3411     player->GfxAction = ACTION_DEFAULT;
3412     player->Frame = 0;
3413     player->StepFrame = 0;
3414
3415     player->initial_element = player->element_nr;
3416     player->artwork_element =
3417       (level.use_artwork_element[i] ? level.artwork_element[i] :
3418        player->element_nr);
3419     player->use_murphy = FALSE;
3420
3421     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3422     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3423
3424     player->gravity = level.initial_player_gravity[i];
3425
3426     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3427
3428     player->actual_frame_counter = 0;
3429
3430     player->step_counter = 0;
3431
3432     player->last_move_dir = initial_move_dir;
3433
3434     player->is_active = FALSE;
3435
3436     player->is_waiting = FALSE;
3437     player->is_moving = FALSE;
3438     player->is_auto_moving = FALSE;
3439     player->is_digging = FALSE;
3440     player->is_snapping = FALSE;
3441     player->is_collecting = FALSE;
3442     player->is_pushing = FALSE;
3443     player->is_switching = FALSE;
3444     player->is_dropping = FALSE;
3445     player->is_dropping_pressed = FALSE;
3446
3447     player->is_bored = FALSE;
3448     player->is_sleeping = FALSE;
3449
3450     player->was_waiting = TRUE;
3451     player->was_moving = FALSE;
3452     player->was_snapping = FALSE;
3453     player->was_dropping = FALSE;
3454
3455     player->force_dropping = FALSE;
3456
3457     player->frame_counter_bored = -1;
3458     player->frame_counter_sleeping = -1;
3459
3460     player->anim_delay_counter = 0;
3461     player->post_delay_counter = 0;
3462
3463     player->dir_waiting = initial_move_dir;
3464     player->action_waiting = ACTION_DEFAULT;
3465     player->last_action_waiting = ACTION_DEFAULT;
3466     player->special_action_bored = ACTION_DEFAULT;
3467     player->special_action_sleeping = ACTION_DEFAULT;
3468
3469     player->switch_x = -1;
3470     player->switch_y = -1;
3471
3472     player->drop_x = -1;
3473     player->drop_y = -1;
3474
3475     player->show_envelope = 0;
3476
3477     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3478
3479     player->push_delay       = -1;      /* initialized when pushing starts */
3480     player->push_delay_value = game.initial_push_delay_value;
3481
3482     player->drop_delay = 0;
3483     player->drop_pressed_delay = 0;
3484
3485     player->last_jx = -1;
3486     player->last_jy = -1;
3487     player->jx = -1;
3488     player->jy = -1;
3489
3490     player->shield_normal_time_left = 0;
3491     player->shield_deadly_time_left = 0;
3492
3493     player->inventory_infinite_element = EL_UNDEFINED;
3494     player->inventory_size = 0;
3495
3496     if (level.use_initial_inventory[i])
3497     {
3498       for (j = 0; j < level.initial_inventory_size[i]; j++)
3499       {
3500         int element = level.initial_inventory_content[i][j];
3501         int collect_count = element_info[element].collect_count_initial;
3502         int k;
3503
3504         if (!IS_CUSTOM_ELEMENT(element))
3505           collect_count = 1;
3506
3507         if (collect_count == 0)
3508           player->inventory_infinite_element = element;
3509         else
3510           for (k = 0; k < collect_count; k++)
3511             if (player->inventory_size < MAX_INVENTORY_SIZE)
3512               player->inventory_element[player->inventory_size++] = element;
3513       }
3514     }
3515
3516     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3517     SnapField(player, 0, 0);
3518
3519     player->LevelSolved = FALSE;
3520     player->GameOver = FALSE;
3521
3522     player->LevelSolved_GameWon = FALSE;
3523     player->LevelSolved_GameEnd = FALSE;
3524     player->LevelSolved_PanelOff = FALSE;
3525     player->LevelSolved_SaveTape = FALSE;
3526     player->LevelSolved_SaveScore = FALSE;
3527
3528     player->LevelSolved_CountingTime = 0;
3529     player->LevelSolved_CountingScore = 0;
3530     player->LevelSolved_CountingHealth = 0;
3531
3532     map_player_action[i] = i;
3533   }
3534
3535   network_player_action_received = FALSE;
3536
3537   /* initial null action */
3538   if (network_playing)
3539     SendToServer_MovePlayer(MV_NONE);
3540
3541   ZX = ZY = -1;
3542   ExitX = ExitY = -1;
3543
3544   FrameCounter = 0;
3545   TimeFrames = 0;
3546   TimePlayed = 0;
3547   TimeLeft = level.time;
3548   TapeTime = 0;
3549
3550   ScreenMovDir = MV_NONE;
3551   ScreenMovPos = 0;
3552   ScreenGfxPos = 0;
3553
3554   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3555
3556   AllPlayersGone = FALSE;
3557
3558   game.no_time_limit = (level.time == 0);
3559
3560   game.yamyam_content_nr = 0;
3561   game.robot_wheel_active = FALSE;
3562   game.magic_wall_active = FALSE;
3563   game.magic_wall_time_left = 0;
3564   game.light_time_left = 0;
3565   game.timegate_time_left = 0;
3566   game.switchgate_pos = 0;
3567   game.wind_direction = level.wind_direction_initial;
3568
3569   game.lenses_time_left = 0;
3570   game.magnify_time_left = 0;
3571
3572   game.ball_state = level.ball_state_initial;
3573   game.ball_content_nr = 0;
3574
3575   game.envelope_active = FALSE;
3576
3577   for (i = 0; i < NUM_BELTS; i++)
3578   {
3579     game.belt_dir[i] = MV_NONE;
3580     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3581   }
3582
3583   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3584     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3585
3586 #if DEBUG_INIT_PLAYER
3587   DebugPrintPlayerStatus("Player status at level initialization");
3588 #endif
3589
3590   SCAN_PLAYFIELD(x, y)
3591   {
3592     Feld[x][y] = level.field[x][y];
3593     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3594     ChangeDelay[x][y] = 0;
3595     ChangePage[x][y] = -1;
3596     CustomValue[x][y] = 0;              /* initialized in InitField() */
3597     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3598     AmoebaNr[x][y] = 0;
3599     WasJustMoving[x][y] = 0;
3600     WasJustFalling[x][y] = 0;
3601     CheckCollision[x][y] = 0;
3602     CheckImpact[x][y] = 0;
3603     Stop[x][y] = FALSE;
3604     Pushed[x][y] = FALSE;
3605
3606     ChangeCount[x][y] = 0;
3607     ChangeEvent[x][y] = -1;
3608
3609     ExplodePhase[x][y] = 0;
3610     ExplodeDelay[x][y] = 0;
3611     ExplodeField[x][y] = EX_TYPE_NONE;
3612
3613     RunnerVisit[x][y] = 0;
3614     PlayerVisit[x][y] = 0;
3615
3616     GfxFrame[x][y] = 0;
3617     GfxRandom[x][y] = INIT_GFX_RANDOM();
3618     GfxElement[x][y] = EL_UNDEFINED;
3619     GfxAction[x][y] = ACTION_DEFAULT;
3620     GfxDir[x][y] = MV_NONE;
3621     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3622   }
3623
3624   SCAN_PLAYFIELD(x, y)
3625   {
3626     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3627       emulate_bd = FALSE;
3628     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3629       emulate_sb = FALSE;
3630     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3631       emulate_sp = FALSE;
3632
3633     InitField(x, y, TRUE);
3634
3635     ResetGfxAnimation(x, y);
3636   }
3637
3638   InitBeltMovement();
3639
3640   for (i = 0; i < MAX_PLAYERS; i++)
3641   {
3642     struct PlayerInfo *player = &stored_player[i];
3643
3644     /* set number of special actions for bored and sleeping animation */
3645     player->num_special_action_bored =
3646       get_num_special_action(player->artwork_element,
3647                              ACTION_BORING_1, ACTION_BORING_LAST);
3648     player->num_special_action_sleeping =
3649       get_num_special_action(player->artwork_element,
3650                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3651   }
3652
3653   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3654                     emulate_sb ? EMU_SOKOBAN :
3655                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3656
3657   /* initialize type of slippery elements */
3658   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3659   {
3660     if (!IS_CUSTOM_ELEMENT(i))
3661     {
3662       /* default: elements slip down either to the left or right randomly */
3663       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3664
3665       /* SP style elements prefer to slip down on the left side */
3666       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3667         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3668
3669       /* BD style elements prefer to slip down on the left side */
3670       if (game.emulation == EMU_BOULDERDASH)
3671         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3672     }
3673   }
3674
3675   /* initialize explosion and ignition delay */
3676   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3677   {
3678     if (!IS_CUSTOM_ELEMENT(i))
3679     {
3680       int num_phase = 8;
3681       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3682                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3683                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3684       int last_phase = (num_phase + 1) * delay;
3685       int half_phase = (num_phase / 2) * delay;
3686
3687       element_info[i].explosion_delay = last_phase - 1;
3688       element_info[i].ignition_delay = half_phase;
3689
3690       if (i == EL_BLACK_ORB)
3691         element_info[i].ignition_delay = 1;
3692     }
3693   }
3694
3695   /* correct non-moving belts to start moving left */
3696   for (i = 0; i < NUM_BELTS; i++)
3697     if (game.belt_dir[i] == MV_NONE)
3698       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3699
3700 #if USE_NEW_PLAYER_ASSIGNMENTS
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     stored_player[i].connected = FALSE;
3704
3705     /* in network game mode, the local player might not be the first player */
3706     if (stored_player[i].connected_locally)
3707       local_player = &stored_player[i];
3708   }
3709
3710   if (!network.enabled)
3711     local_player->connected = TRUE;
3712
3713   if (tape.playing)
3714   {
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716       stored_player[i].connected = tape.player_participates[i];
3717   }
3718   else if (network.enabled)
3719   {
3720     /* add team mode players connected over the network (needed for correct
3721        assignment of player figures from level to locally playing players) */
3722
3723     for (i = 0; i < MAX_PLAYERS; i++)
3724       if (stored_player[i].connected_network)
3725         stored_player[i].connected = TRUE;
3726   }
3727   else if (game.team_mode)
3728   {
3729     /* try to guess locally connected team mode players (needed for correct
3730        assignment of player figures from level to locally playing players) */
3731
3732     for (i = 0; i < MAX_PLAYERS; i++)
3733       if (setup.input[i].use_joystick ||
3734           setup.input[i].key.left != KSYM_UNDEFINED)
3735         stored_player[i].connected = TRUE;
3736   }
3737
3738 #if DEBUG_INIT_PLAYER
3739   DebugPrintPlayerStatus("Player status after level initialization");
3740 #endif
3741
3742 #if DEBUG_INIT_PLAYER
3743   if (options.debug)
3744     printf("Reassigning players ...\n");
3745 #endif
3746
3747   /* check if any connected player was not found in playfield */
3748   for (i = 0; i < MAX_PLAYERS; i++)
3749   {
3750     struct PlayerInfo *player = &stored_player[i];
3751
3752     if (player->connected && !player->present)
3753     {
3754       struct PlayerInfo *field_player = NULL;
3755
3756 #if DEBUG_INIT_PLAYER
3757       if (options.debug)
3758         printf("- looking for field player for player %d ...\n", i + 1);
3759 #endif
3760
3761       /* assign first free player found that is present in the playfield */
3762
3763       /* first try: look for unmapped playfield player that is not connected */
3764       for (j = 0; j < MAX_PLAYERS; j++)
3765         if (field_player == NULL &&
3766             stored_player[j].present &&
3767             !stored_player[j].mapped &&
3768             !stored_player[j].connected)
3769           field_player = &stored_player[j];
3770
3771       /* second try: look for *any* unmapped playfield player */
3772       for (j = 0; j < MAX_PLAYERS; j++)
3773         if (field_player == NULL &&
3774             stored_player[j].present &&
3775             !stored_player[j].mapped)
3776           field_player = &stored_player[j];
3777
3778       if (field_player != NULL)
3779       {
3780         int jx = field_player->jx, jy = field_player->jy;
3781
3782 #if DEBUG_INIT_PLAYER
3783         if (options.debug)
3784           printf("- found player %d\n", field_player->index_nr + 1);
3785 #endif
3786
3787         player->present = FALSE;
3788         player->active = FALSE;
3789
3790         field_player->present = TRUE;
3791         field_player->active = TRUE;
3792
3793         /*
3794         player->initial_element = field_player->initial_element;
3795         player->artwork_element = field_player->artwork_element;
3796
3797         player->block_last_field       = field_player->block_last_field;
3798         player->block_delay_adjustment = field_player->block_delay_adjustment;
3799         */
3800
3801         StorePlayer[jx][jy] = field_player->element_nr;
3802
3803         field_player->jx = field_player->last_jx = jx;
3804         field_player->jy = field_player->last_jy = jy;
3805
3806         if (local_player == player)
3807           local_player = field_player;
3808
3809         map_player_action[field_player->index_nr] = i;
3810
3811         field_player->mapped = TRUE;
3812
3813 #if DEBUG_INIT_PLAYER
3814         if (options.debug)
3815           printf("- map_player_action[%d] == %d\n",
3816                  field_player->index_nr + 1, i + 1);
3817 #endif
3818       }
3819     }
3820
3821     if (player->connected && player->present)
3822       player->mapped = TRUE;
3823   }
3824
3825 #if DEBUG_INIT_PLAYER
3826   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
3827 #endif
3828
3829 #else
3830
3831   /* check if any connected player was not found in playfield */
3832   for (i = 0; i < MAX_PLAYERS; i++)
3833   {
3834     struct PlayerInfo *player = &stored_player[i];
3835
3836     if (player->connected && !player->present)
3837     {
3838       for (j = 0; j < MAX_PLAYERS; j++)
3839       {
3840         struct PlayerInfo *field_player = &stored_player[j];
3841         int jx = field_player->jx, jy = field_player->jy;
3842
3843         /* assign first free player found that is present in the playfield */
3844         if (field_player->present && !field_player->connected)
3845         {
3846           player->present = TRUE;
3847           player->active = TRUE;
3848
3849           field_player->present = FALSE;
3850           field_player->active = FALSE;
3851
3852           player->initial_element = field_player->initial_element;
3853           player->artwork_element = field_player->artwork_element;
3854
3855           player->block_last_field       = field_player->block_last_field;
3856           player->block_delay_adjustment = field_player->block_delay_adjustment;
3857
3858           StorePlayer[jx][jy] = player->element_nr;
3859
3860           player->jx = player->last_jx = jx;
3861           player->jy = player->last_jy = jy;
3862
3863           break;
3864         }
3865       }
3866     }
3867   }
3868 #endif
3869
3870 #if 0
3871   printf("::: local_player->present == %d\n", local_player->present);
3872 #endif
3873
3874   /* set focus to local player for network games, else to all players */
3875   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3876   game.centered_player_nr_next = game.centered_player_nr;
3877   game.set_centered_player = FALSE;
3878
3879   if (network_playing && tape.recording)
3880   {
3881     /* store client dependent player focus when recording network games */
3882     tape.centered_player_nr_next = game.centered_player_nr_next;
3883     tape.set_centered_player = TRUE;
3884   }
3885
3886   if (tape.playing)
3887   {
3888     /* when playing a tape, eliminate all players who do not participate */
3889
3890 #if USE_NEW_PLAYER_ASSIGNMENTS
3891
3892     if (!game.team_mode)
3893     {
3894       for (i = 0; i < MAX_PLAYERS; i++)
3895       {
3896         if (stored_player[i].active &&
3897             !tape.player_participates[map_player_action[i]])
3898         {
3899           struct PlayerInfo *player = &stored_player[i];
3900           int jx = player->jx, jy = player->jy;
3901
3902 #if DEBUG_INIT_PLAYER
3903           if (options.debug)
3904             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3905 #endif
3906
3907           player->active = FALSE;
3908           StorePlayer[jx][jy] = 0;
3909           Feld[jx][jy] = EL_EMPTY;
3910         }
3911       }
3912     }
3913
3914 #else
3915
3916     for (i = 0; i < MAX_PLAYERS; i++)
3917     {
3918       if (stored_player[i].active &&
3919           !tape.player_participates[i])
3920       {
3921         struct PlayerInfo *player = &stored_player[i];
3922         int jx = player->jx, jy = player->jy;
3923
3924         player->active = FALSE;
3925         StorePlayer[jx][jy] = 0;
3926         Feld[jx][jy] = EL_EMPTY;
3927       }
3928     }
3929 #endif
3930   }
3931   else if (!network.enabled && !game.team_mode)         /* && !tape.playing */
3932   {
3933     /* when in single player mode, eliminate all but the local player */
3934
3935     for (i = 0; i < MAX_PLAYERS; i++)
3936     {
3937       struct PlayerInfo *player = &stored_player[i];
3938
3939       if (player->active && player != local_player)
3940       {
3941         int jx = player->jx, jy = player->jy;
3942
3943         player->active = FALSE;
3944         player->present = FALSE;
3945
3946         StorePlayer[jx][jy] = 0;
3947         Feld[jx][jy] = EL_EMPTY;
3948       }
3949     }
3950   }
3951
3952   /* when recording the game, store which players take part in the game */
3953   if (tape.recording)
3954   {
3955 #if USE_NEW_PLAYER_ASSIGNMENTS
3956     for (i = 0; i < MAX_PLAYERS; i++)
3957       if (stored_player[i].connected)
3958         tape.player_participates[i] = TRUE;
3959 #else
3960     for (i = 0; i < MAX_PLAYERS; i++)
3961       if (stored_player[i].active)
3962         tape.player_participates[i] = TRUE;
3963 #endif
3964   }
3965
3966 #if DEBUG_INIT_PLAYER
3967   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
3968 #endif
3969
3970   if (BorderElement == EL_EMPTY)
3971   {
3972     SBX_Left = 0;
3973     SBX_Right = lev_fieldx - SCR_FIELDX;
3974     SBY_Upper = 0;
3975     SBY_Lower = lev_fieldy - SCR_FIELDY;
3976   }
3977   else
3978   {
3979     SBX_Left = -1;
3980     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3981     SBY_Upper = -1;
3982     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3983   }
3984
3985   if (full_lev_fieldx <= SCR_FIELDX)
3986     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3987   if (full_lev_fieldy <= SCR_FIELDY)
3988     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3989
3990   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3991     SBX_Left--;
3992   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3993     SBY_Upper--;
3994
3995   /* if local player not found, look for custom element that might create
3996      the player (make some assumptions about the right custom element) */
3997   if (!local_player->present)
3998   {
3999     int start_x = 0, start_y = 0;
4000     int found_rating = 0;
4001     int found_element = EL_UNDEFINED;
4002     int player_nr = local_player->index_nr;
4003
4004     SCAN_PLAYFIELD(x, y)
4005     {
4006       int element = Feld[x][y];
4007       int content;
4008       int xx, yy;
4009       boolean is_player;
4010
4011       if (level.use_start_element[player_nr] &&
4012           level.start_element[player_nr] == element &&
4013           found_rating < 4)
4014       {
4015         start_x = x;
4016         start_y = y;
4017
4018         found_rating = 4;
4019         found_element = element;
4020       }
4021
4022       if (!IS_CUSTOM_ELEMENT(element))
4023         continue;
4024
4025       if (CAN_CHANGE(element))
4026       {
4027         for (i = 0; i < element_info[element].num_change_pages; i++)
4028         {
4029           /* check for player created from custom element as single target */
4030           content = element_info[element].change_page[i].target_element;
4031           is_player = ELEM_IS_PLAYER(content);
4032
4033           if (is_player && (found_rating < 3 ||
4034                             (found_rating == 3 && element < found_element)))
4035           {
4036             start_x = x;
4037             start_y = y;
4038
4039             found_rating = 3;
4040             found_element = element;
4041           }
4042         }
4043       }
4044
4045       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4046       {
4047         /* check for player created from custom element as explosion content */
4048         content = element_info[element].content.e[xx][yy];
4049         is_player = ELEM_IS_PLAYER(content);
4050
4051         if (is_player && (found_rating < 2 ||
4052                           (found_rating == 2 && element < found_element)))
4053         {
4054           start_x = x + xx - 1;
4055           start_y = y + yy - 1;
4056
4057           found_rating = 2;
4058           found_element = element;
4059         }
4060
4061         if (!CAN_CHANGE(element))
4062           continue;
4063
4064         for (i = 0; i < element_info[element].num_change_pages; i++)
4065         {
4066           /* check for player created from custom element as extended target */
4067           content =
4068             element_info[element].change_page[i].target_content.e[xx][yy];
4069
4070           is_player = ELEM_IS_PLAYER(content);
4071
4072           if (is_player && (found_rating < 1 ||
4073                             (found_rating == 1 && element < found_element)))
4074           {
4075             start_x = x + xx - 1;
4076             start_y = y + yy - 1;
4077
4078             found_rating = 1;
4079             found_element = element;
4080           }
4081         }
4082       }
4083     }
4084
4085     scroll_x = SCROLL_POSITION_X(start_x);
4086     scroll_y = SCROLL_POSITION_Y(start_y);
4087   }
4088   else
4089   {
4090     scroll_x = SCROLL_POSITION_X(local_player->jx);
4091     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4092   }
4093
4094   /* !!! FIX THIS (START) !!! */
4095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4096   {
4097     InitGameEngine_EM();
4098   }
4099   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4100   {
4101     InitGameEngine_SP();
4102   }
4103   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4104   {
4105     InitGameEngine_MM();
4106   }
4107   else
4108   {
4109     DrawLevel(REDRAW_FIELD);
4110     DrawAllPlayers();
4111
4112     /* after drawing the level, correct some elements */
4113     if (game.timegate_time_left == 0)
4114       CloseAllOpenTimegates();
4115   }
4116
4117   /* blit playfield from scroll buffer to normal back buffer for fading in */
4118   BlitScreenToBitmap(backbuffer);
4119   /* !!! FIX THIS (END) !!! */
4120
4121   DrawMaskedBorder(fade_mask);
4122
4123   FadeIn(fade_mask);
4124
4125 #if 1
4126   // full screen redraw is required at this point in the following cases:
4127   // - special editor door undrawn when game was started from level editor
4128   // - drawing area (playfield) was changed and has to be removed completely
4129   redraw_mask = REDRAW_ALL;
4130   BackToFront();
4131 #endif
4132
4133   if (!game.restart_level)
4134   {
4135     /* copy default game door content to main double buffer */
4136
4137     /* !!! CHECK AGAIN !!! */
4138     SetPanelBackground();
4139     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4140     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4141   }
4142
4143   SetPanelBackground();
4144   SetDrawBackgroundMask(REDRAW_DOOR_1);
4145
4146   UpdateAndDisplayGameControlValues();
4147
4148   if (!game.restart_level)
4149   {
4150     UnmapGameButtons();
4151     UnmapTapeButtons();
4152
4153     FreeGameButtons();
4154     CreateGameButtons();
4155
4156     MapGameButtons();
4157     MapTapeButtons();
4158
4159     /* copy actual game door content to door double buffer for OpenDoor() */
4160     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4161
4162     OpenDoor(DOOR_OPEN_ALL);
4163
4164     KeyboardAutoRepeatOffUnlessAutoplay();
4165
4166 #if DEBUG_INIT_PLAYER
4167     DebugPrintPlayerStatus("Player status (final)");
4168 #endif
4169   }
4170
4171   UnmapAllGadgets();
4172
4173   MapGameButtons();
4174   MapTapeButtons();
4175
4176   if (!game.restart_level && !tape.playing)
4177   {
4178     LevelStats_incPlayed(level_nr);
4179
4180     SaveLevelSetup_SeriesInfo();
4181   }
4182
4183   game.restart_level = FALSE;
4184   game.restart_game_message = NULL;
4185
4186   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4187     InitGameActions_MM();
4188
4189   SaveEngineSnapshotToListInitial();
4190
4191   if (!game.restart_level)
4192   {
4193     PlaySound(SND_GAME_STARTING);
4194
4195     if (setup.sound_music)
4196       PlayLevelMusic();
4197   }
4198 }
4199
4200 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4201                         int actual_player_x, int actual_player_y)
4202 {
4203   /* this is used for non-R'n'D game engines to update certain engine values */
4204
4205   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4206   {
4207     actual_player_x = correctLevelPosX_EM(actual_player_x);
4208     actual_player_y = correctLevelPosY_EM(actual_player_y);
4209   }
4210
4211   /* needed to determine if sounds are played within the visible screen area */
4212   scroll_x = actual_scroll_x;
4213   scroll_y = actual_scroll_y;
4214
4215   /* needed to get player position for "follow finger" playing input method */
4216   local_player->jx = actual_player_x;
4217   local_player->jy = actual_player_y;
4218 }
4219
4220 void InitMovDir(int x, int y)
4221 {
4222   int i, element = Feld[x][y];
4223   static int xy[4][2] =
4224   {
4225     {  0, +1 },
4226     { +1,  0 },
4227     {  0, -1 },
4228     { -1,  0 }
4229   };
4230   static int direction[3][4] =
4231   {
4232     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4233     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4234     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4235   };
4236
4237   switch (element)
4238   {
4239     case EL_BUG_RIGHT:
4240     case EL_BUG_UP:
4241     case EL_BUG_LEFT:
4242     case EL_BUG_DOWN:
4243       Feld[x][y] = EL_BUG;
4244       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4245       break;
4246
4247     case EL_SPACESHIP_RIGHT:
4248     case EL_SPACESHIP_UP:
4249     case EL_SPACESHIP_LEFT:
4250     case EL_SPACESHIP_DOWN:
4251       Feld[x][y] = EL_SPACESHIP;
4252       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4253       break;
4254
4255     case EL_BD_BUTTERFLY_RIGHT:
4256     case EL_BD_BUTTERFLY_UP:
4257     case EL_BD_BUTTERFLY_LEFT:
4258     case EL_BD_BUTTERFLY_DOWN:
4259       Feld[x][y] = EL_BD_BUTTERFLY;
4260       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4261       break;
4262
4263     case EL_BD_FIREFLY_RIGHT:
4264     case EL_BD_FIREFLY_UP:
4265     case EL_BD_FIREFLY_LEFT:
4266     case EL_BD_FIREFLY_DOWN:
4267       Feld[x][y] = EL_BD_FIREFLY;
4268       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4269       break;
4270
4271     case EL_PACMAN_RIGHT:
4272     case EL_PACMAN_UP:
4273     case EL_PACMAN_LEFT:
4274     case EL_PACMAN_DOWN:
4275       Feld[x][y] = EL_PACMAN;
4276       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4277       break;
4278
4279     case EL_YAMYAM_LEFT:
4280     case EL_YAMYAM_RIGHT:
4281     case EL_YAMYAM_UP:
4282     case EL_YAMYAM_DOWN:
4283       Feld[x][y] = EL_YAMYAM;
4284       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4285       break;
4286
4287     case EL_SP_SNIKSNAK:
4288       MovDir[x][y] = MV_UP;
4289       break;
4290
4291     case EL_SP_ELECTRON:
4292       MovDir[x][y] = MV_LEFT;
4293       break;
4294
4295     case EL_MOLE_LEFT:
4296     case EL_MOLE_RIGHT:
4297     case EL_MOLE_UP:
4298     case EL_MOLE_DOWN:
4299       Feld[x][y] = EL_MOLE;
4300       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4301       break;
4302
4303     default:
4304       if (IS_CUSTOM_ELEMENT(element))
4305       {
4306         struct ElementInfo *ei = &element_info[element];
4307         int move_direction_initial = ei->move_direction_initial;
4308         int move_pattern = ei->move_pattern;
4309
4310         if (move_direction_initial == MV_START_PREVIOUS)
4311         {
4312           if (MovDir[x][y] != MV_NONE)
4313             return;
4314
4315           move_direction_initial = MV_START_AUTOMATIC;
4316         }
4317
4318         if (move_direction_initial == MV_START_RANDOM)
4319           MovDir[x][y] = 1 << RND(4);
4320         else if (move_direction_initial & MV_ANY_DIRECTION)
4321           MovDir[x][y] = move_direction_initial;
4322         else if (move_pattern == MV_ALL_DIRECTIONS ||
4323                  move_pattern == MV_TURNING_LEFT ||
4324                  move_pattern == MV_TURNING_RIGHT ||
4325                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4326                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4327                  move_pattern == MV_TURNING_RANDOM)
4328           MovDir[x][y] = 1 << RND(4);
4329         else if (move_pattern == MV_HORIZONTAL)
4330           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4331         else if (move_pattern == MV_VERTICAL)
4332           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4333         else if (move_pattern & MV_ANY_DIRECTION)
4334           MovDir[x][y] = element_info[element].move_pattern;
4335         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4336                  move_pattern == MV_ALONG_RIGHT_SIDE)
4337         {
4338           /* use random direction as default start direction */
4339           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4340             MovDir[x][y] = 1 << RND(4);
4341
4342           for (i = 0; i < NUM_DIRECTIONS; i++)
4343           {
4344             int x1 = x + xy[i][0];
4345             int y1 = y + xy[i][1];
4346
4347             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4348             {
4349               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4350                 MovDir[x][y] = direction[0][i];
4351               else
4352                 MovDir[x][y] = direction[1][i];
4353
4354               break;
4355             }
4356           }
4357         }                
4358       }
4359       else
4360       {
4361         MovDir[x][y] = 1 << RND(4);
4362
4363         if (element != EL_BUG &&
4364             element != EL_SPACESHIP &&
4365             element != EL_BD_BUTTERFLY &&
4366             element != EL_BD_FIREFLY)
4367           break;
4368
4369         for (i = 0; i < NUM_DIRECTIONS; i++)
4370         {
4371           int x1 = x + xy[i][0];
4372           int y1 = y + xy[i][1];
4373
4374           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4375           {
4376             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4377             {
4378               MovDir[x][y] = direction[0][i];
4379               break;
4380             }
4381             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4382                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4383             {
4384               MovDir[x][y] = direction[1][i];
4385               break;
4386             }
4387           }
4388         }
4389       }
4390       break;
4391   }
4392
4393   GfxDir[x][y] = MovDir[x][y];
4394 }
4395
4396 void InitAmoebaNr(int x, int y)
4397 {
4398   int i;
4399   int group_nr = AmoebeNachbarNr(x, y);
4400
4401   if (group_nr == 0)
4402   {
4403     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4404     {
4405       if (AmoebaCnt[i] == 0)
4406       {
4407         group_nr = i;
4408         break;
4409       }
4410     }
4411   }
4412
4413   AmoebaNr[x][y] = group_nr;
4414   AmoebaCnt[group_nr]++;
4415   AmoebaCnt2[group_nr]++;
4416 }
4417
4418 static void PlayerWins(struct PlayerInfo *player)
4419 {
4420   player->LevelSolved = TRUE;
4421   player->GameOver = TRUE;
4422
4423   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4424                          level.native_em_level->lev->score :
4425                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4426                          game_mm.score :
4427                          player->score);
4428   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4429                           MM_HEALTH(game_mm.laser_overload_value) :
4430                           player->health);
4431
4432   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4433                                       TimeLeft);
4434   player->LevelSolved_CountingScore = player->score_final;
4435   player->LevelSolved_CountingHealth = player->health_final;
4436 }
4437
4438 void GameWon()
4439 {
4440   static int time_count_steps;
4441   static int time, time_final;
4442   static int score, score_final;
4443   static int health, health_final;
4444   static int game_over_delay_1 = 0;
4445   static int game_over_delay_2 = 0;
4446   static int game_over_delay_3 = 0;
4447   int game_over_delay_value_1 = 50;
4448   int game_over_delay_value_2 = 25;
4449   int game_over_delay_value_3 = 50;
4450
4451   if (!local_player->LevelSolved_GameWon)
4452   {
4453     int i;
4454
4455     /* do not start end game actions before the player stops moving (to exit) */
4456     if (local_player->MovPos)
4457       return;
4458
4459     local_player->LevelSolved_GameWon = TRUE;
4460     local_player->LevelSolved_SaveTape = tape.recording;
4461     local_player->LevelSolved_SaveScore = !tape.playing;
4462
4463     if (!tape.playing)
4464     {
4465       LevelStats_incSolved(level_nr);
4466
4467       SaveLevelSetup_SeriesInfo();
4468     }
4469
4470     if (tape.auto_play)         /* tape might already be stopped here */
4471       tape.auto_play_level_solved = TRUE;
4472
4473     TapeStop();
4474
4475     game_over_delay_1 = 0;
4476     game_over_delay_2 = 0;
4477     game_over_delay_3 = game_over_delay_value_3;
4478
4479     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4480     score = score_final = local_player->score_final;
4481     health = health_final = local_player->health_final;
4482
4483     if (level.score[SC_TIME_BONUS] > 0)
4484     {
4485       if (TimeLeft > 0)
4486       {
4487         time_final = 0;
4488         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4489       }
4490       else if (game.no_time_limit && TimePlayed < 999)
4491       {
4492         time_final = 999;
4493         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4494       }
4495
4496       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4497
4498       game_over_delay_1 = game_over_delay_value_1;
4499
4500       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4501       {
4502         health_final = 0;
4503         score_final += health * level.score[SC_TIME_BONUS];
4504
4505         game_over_delay_2 = game_over_delay_value_2;
4506       }
4507
4508       local_player->score_final = score_final;
4509       local_player->health_final = health_final;
4510     }
4511
4512     if (level_editor_test_game)
4513     {
4514       time = time_final;
4515       score = score_final;
4516
4517       local_player->LevelSolved_CountingTime = time;
4518       local_player->LevelSolved_CountingScore = score;
4519
4520       game_panel_controls[GAME_PANEL_TIME].value = time;
4521       game_panel_controls[GAME_PANEL_SCORE].value = score;
4522
4523       DisplayGameControlValues();
4524     }
4525
4526     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4527     {
4528       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4529       {
4530         /* close exit door after last player */
4531         if ((AllPlayersGone &&
4532              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4533               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4534               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4535             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4536             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4537         {
4538           int element = Feld[ExitX][ExitY];
4539
4540           Feld[ExitX][ExitY] =
4541             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4542              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4543              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4544              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4545              EL_EM_STEEL_EXIT_CLOSING);
4546
4547           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4548         }
4549
4550         /* player disappears */
4551         DrawLevelField(ExitX, ExitY);
4552       }
4553
4554       for (i = 0; i < MAX_PLAYERS; i++)
4555       {
4556         struct PlayerInfo *player = &stored_player[i];
4557
4558         if (player->present)
4559         {
4560           RemovePlayer(player);
4561
4562           /* player disappears */
4563           DrawLevelField(player->jx, player->jy);
4564         }
4565       }
4566     }
4567
4568     PlaySound(SND_GAME_WINNING);
4569   }
4570
4571   if (game_over_delay_1 > 0)
4572   {
4573     game_over_delay_1--;
4574
4575     return;
4576   }
4577
4578   if (time != time_final)
4579   {
4580     int time_to_go = ABS(time_final - time);
4581     int time_count_dir = (time < time_final ? +1 : -1);
4582
4583     if (time_to_go < time_count_steps)
4584       time_count_steps = 1;
4585
4586     time  += time_count_steps * time_count_dir;
4587     score += time_count_steps * level.score[SC_TIME_BONUS];
4588
4589     local_player->LevelSolved_CountingTime = time;
4590     local_player->LevelSolved_CountingScore = score;
4591
4592     game_panel_controls[GAME_PANEL_TIME].value = time;
4593     game_panel_controls[GAME_PANEL_SCORE].value = score;
4594
4595     DisplayGameControlValues();
4596
4597     if (time == time_final)
4598       StopSound(SND_GAME_LEVELTIME_BONUS);
4599     else if (setup.sound_loops)
4600       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4601     else
4602       PlaySound(SND_GAME_LEVELTIME_BONUS);
4603
4604     return;
4605   }
4606
4607   if (game_over_delay_2 > 0)
4608   {
4609     game_over_delay_2--;
4610
4611     return;
4612   }
4613
4614   if (health != health_final)
4615   {
4616     int health_count_dir = (health < health_final ? +1 : -1);
4617
4618     health += health_count_dir;
4619     score  += level.score[SC_TIME_BONUS];
4620
4621     local_player->LevelSolved_CountingHealth = health;
4622     local_player->LevelSolved_CountingScore = score;
4623
4624     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4625     game_panel_controls[GAME_PANEL_SCORE].value = score;
4626
4627     DisplayGameControlValues();
4628
4629     if (health == health_final)
4630       StopSound(SND_GAME_LEVELTIME_BONUS);
4631     else if (setup.sound_loops)
4632       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4633     else
4634       PlaySound(SND_GAME_LEVELTIME_BONUS);
4635
4636     return;
4637   }
4638
4639   local_player->LevelSolved_PanelOff = TRUE;
4640
4641   if (game_over_delay_3 > 0)
4642   {
4643     game_over_delay_3--;
4644
4645     return;
4646   }
4647
4648   GameEnd();
4649 }
4650
4651 void GameEnd()
4652 {
4653   int hi_pos;
4654   int last_level_nr = level_nr;
4655
4656   local_player->LevelSolved_GameEnd = TRUE;
4657
4658   if (local_player->LevelSolved_SaveTape)
4659   {
4660     /* make sure that request dialog to save tape does not open door again */
4661     if (!global.use_envelope_request)
4662       CloseDoor(DOOR_CLOSE_1);
4663
4664     SaveTapeChecked_LevelSolved(tape.level_nr);         /* ask to save tape */
4665   }
4666
4667   /* if no tape is to be saved, close both doors simultaneously */
4668   CloseDoor(DOOR_CLOSE_ALL);
4669
4670   if (level_editor_test_game)
4671   {
4672     SetGameStatus(GAME_MODE_MAIN);
4673
4674     DrawMainMenu();
4675
4676     return;
4677   }
4678
4679   if (!local_player->LevelSolved_SaveScore)
4680   {
4681     SetGameStatus(GAME_MODE_MAIN);
4682
4683     DrawMainMenu();
4684
4685     return;
4686   }
4687
4688   if (level_nr == leveldir_current->handicap_level)
4689   {
4690     leveldir_current->handicap_level++;
4691
4692     SaveLevelSetup_SeriesInfo();
4693   }
4694
4695   if (setup.increment_levels &&
4696       level_nr < leveldir_current->last_level)
4697   {
4698     level_nr++;         /* advance to next level */
4699     TapeErase();        /* start with empty tape */
4700
4701     if (setup.auto_play_next_level)
4702     {
4703       LoadLevel(level_nr);
4704
4705       SaveLevelSetup_SeriesInfo();
4706     }
4707   }
4708
4709   hi_pos = NewHiScore(last_level_nr);
4710
4711   if (hi_pos >= 0 && !setup.skip_scores_after_game)
4712   {
4713     SetGameStatus(GAME_MODE_SCORES);
4714
4715     DrawHallOfFame(last_level_nr, hi_pos);
4716   }
4717   else if (!setup.auto_play_next_level || !setup.increment_levels)
4718   {
4719     SetGameStatus(GAME_MODE_MAIN);
4720
4721     DrawMainMenu();
4722   }
4723   else
4724   {
4725     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
4726   }
4727 }
4728
4729 int NewHiScore(int level_nr)
4730 {
4731   int k, l;
4732   int position = -1;
4733   boolean one_score_entry_per_name = !program.many_scores_per_name;
4734
4735   LoadScore(level_nr);
4736
4737   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4738       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4739     return -1;
4740
4741   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4742   {
4743     if (local_player->score_final > highscore[k].Score)
4744     {
4745       /* player has made it to the hall of fame */
4746
4747       if (k < MAX_SCORE_ENTRIES - 1)
4748       {
4749         int m = MAX_SCORE_ENTRIES - 1;
4750
4751         if (one_score_entry_per_name)
4752         {
4753           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4754             if (strEqual(setup.player_name, highscore[l].Name))
4755               m = l;
4756
4757           if (m == k)   /* player's new highscore overwrites his old one */
4758             goto put_into_list;
4759         }
4760
4761         for (l = m; l > k; l--)
4762         {
4763           strcpy(highscore[l].Name, highscore[l - 1].Name);
4764           highscore[l].Score = highscore[l - 1].Score;
4765         }
4766       }
4767
4768       put_into_list:
4769
4770       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4771       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4772       highscore[k].Score = local_player->score_final; 
4773       position = k;
4774
4775       break;
4776     }
4777     else if (one_score_entry_per_name &&
4778              !strncmp(setup.player_name, highscore[k].Name,
4779                       MAX_PLAYER_NAME_LEN))
4780       break;    /* player already there with a higher score */
4781   }
4782
4783   if (position >= 0) 
4784     SaveScore(level_nr);
4785
4786   return position;
4787 }
4788
4789 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4790 {
4791   int element = Feld[x][y];
4792   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4793   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4794   int horiz_move = (dx != 0);
4795   int sign = (horiz_move ? dx : dy);
4796   int step = sign * element_info[element].move_stepsize;
4797
4798   /* special values for move stepsize for spring and things on conveyor belt */
4799   if (horiz_move)
4800   {
4801     if (CAN_FALL(element) &&
4802         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4803       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4804     else if (element == EL_SPRING)
4805       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4806   }
4807
4808   return step;
4809 }
4810
4811 inline static int getElementMoveStepsize(int x, int y)
4812 {
4813   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4814 }
4815
4816 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4817 {
4818   if (player->GfxAction != action || player->GfxDir != dir)
4819   {
4820     player->GfxAction = action;
4821     player->GfxDir = dir;
4822     player->Frame = 0;
4823     player->StepFrame = 0;
4824   }
4825 }
4826
4827 static void ResetGfxFrame(int x, int y)
4828 {
4829   // profiling showed that "autotest" spends 10~20% of its time in this function
4830   if (DrawingDeactivatedField())
4831     return;
4832
4833   int element = Feld[x][y];
4834   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4835
4836   if (graphic_info[graphic].anim_global_sync)
4837     GfxFrame[x][y] = FrameCounter;
4838   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4839     GfxFrame[x][y] = CustomValue[x][y];
4840   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4841     GfxFrame[x][y] = element_info[element].collect_score;
4842   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4843     GfxFrame[x][y] = ChangeDelay[x][y];
4844 }
4845
4846 static void ResetGfxAnimation(int x, int y)
4847 {
4848   GfxAction[x][y] = ACTION_DEFAULT;
4849   GfxDir[x][y] = MovDir[x][y];
4850   GfxFrame[x][y] = 0;
4851
4852   ResetGfxFrame(x, y);
4853 }
4854
4855 static void ResetRandomAnimationValue(int x, int y)
4856 {
4857   GfxRandom[x][y] = INIT_GFX_RANDOM();
4858 }
4859
4860 void InitMovingField(int x, int y, int direction)
4861 {
4862   int element = Feld[x][y];
4863   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4864   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4865   int newx = x + dx;
4866   int newy = y + dy;
4867   boolean is_moving_before, is_moving_after;
4868
4869   /* check if element was/is moving or being moved before/after mode change */
4870   is_moving_before = (WasJustMoving[x][y] != 0);
4871   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4872
4873   /* reset animation only for moving elements which change direction of moving
4874      or which just started or stopped moving
4875      (else CEs with property "can move" / "not moving" are reset each frame) */
4876   if (is_moving_before != is_moving_after ||
4877       direction != MovDir[x][y])
4878     ResetGfxAnimation(x, y);
4879
4880   MovDir[x][y] = direction;
4881   GfxDir[x][y] = direction;
4882
4883   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4884                      direction == MV_DOWN && CAN_FALL(element) ?
4885                      ACTION_FALLING : ACTION_MOVING);
4886
4887   /* this is needed for CEs with property "can move" / "not moving" */
4888
4889   if (is_moving_after)
4890   {
4891     if (Feld[newx][newy] == EL_EMPTY)
4892       Feld[newx][newy] = EL_BLOCKED;
4893
4894     MovDir[newx][newy] = MovDir[x][y];
4895
4896     CustomValue[newx][newy] = CustomValue[x][y];
4897
4898     GfxFrame[newx][newy] = GfxFrame[x][y];
4899     GfxRandom[newx][newy] = GfxRandom[x][y];
4900     GfxAction[newx][newy] = GfxAction[x][y];
4901     GfxDir[newx][newy] = GfxDir[x][y];
4902   }
4903 }
4904
4905 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4906 {
4907   int direction = MovDir[x][y];
4908   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4909   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4910
4911   *goes_to_x = newx;
4912   *goes_to_y = newy;
4913 }
4914
4915 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4916 {
4917   int oldx = x, oldy = y;
4918   int direction = MovDir[x][y];
4919
4920   if (direction == MV_LEFT)
4921     oldx++;
4922   else if (direction == MV_RIGHT)
4923     oldx--;
4924   else if (direction == MV_UP)
4925     oldy++;
4926   else if (direction == MV_DOWN)
4927     oldy--;
4928
4929   *comes_from_x = oldx;
4930   *comes_from_y = oldy;
4931 }
4932
4933 int MovingOrBlocked2Element(int x, int y)
4934 {
4935   int element = Feld[x][y];
4936
4937   if (element == EL_BLOCKED)
4938   {
4939     int oldx, oldy;
4940
4941     Blocked2Moving(x, y, &oldx, &oldy);
4942     return Feld[oldx][oldy];
4943   }
4944   else
4945     return element;
4946 }
4947
4948 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4949 {
4950   /* like MovingOrBlocked2Element(), but if element is moving
4951      and (x,y) is the field the moving element is just leaving,
4952      return EL_BLOCKED instead of the element value */
4953   int element = Feld[x][y];
4954
4955   if (IS_MOVING(x, y))
4956   {
4957     if (element == EL_BLOCKED)
4958     {
4959       int oldx, oldy;
4960
4961       Blocked2Moving(x, y, &oldx, &oldy);
4962       return Feld[oldx][oldy];
4963     }
4964     else
4965       return EL_BLOCKED;
4966   }
4967   else
4968     return element;
4969 }
4970
4971 static void RemoveField(int x, int y)
4972 {
4973   Feld[x][y] = EL_EMPTY;
4974
4975   MovPos[x][y] = 0;
4976   MovDir[x][y] = 0;
4977   MovDelay[x][y] = 0;
4978
4979   CustomValue[x][y] = 0;
4980
4981   AmoebaNr[x][y] = 0;
4982   ChangeDelay[x][y] = 0;
4983   ChangePage[x][y] = -1;
4984   Pushed[x][y] = FALSE;
4985
4986   GfxElement[x][y] = EL_UNDEFINED;
4987   GfxAction[x][y] = ACTION_DEFAULT;
4988   GfxDir[x][y] = MV_NONE;
4989 }
4990
4991 void RemoveMovingField(int x, int y)
4992 {
4993   int oldx = x, oldy = y, newx = x, newy = y;
4994   int element = Feld[x][y];
4995   int next_element = EL_UNDEFINED;
4996
4997   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4998     return;
4999
5000   if (IS_MOVING(x, y))
5001   {
5002     Moving2Blocked(x, y, &newx, &newy);
5003
5004     if (Feld[newx][newy] != EL_BLOCKED)
5005     {
5006       /* element is moving, but target field is not free (blocked), but
5007          already occupied by something different (example: acid pool);
5008          in this case, only remove the moving field, but not the target */
5009
5010       RemoveField(oldx, oldy);
5011
5012       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5013
5014       TEST_DrawLevelField(oldx, oldy);
5015
5016       return;
5017     }
5018   }
5019   else if (element == EL_BLOCKED)
5020   {
5021     Blocked2Moving(x, y, &oldx, &oldy);
5022     if (!IS_MOVING(oldx, oldy))
5023       return;
5024   }
5025
5026   if (element == EL_BLOCKED &&
5027       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5028        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5029        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5030        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5031        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5032        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5033     next_element = get_next_element(Feld[oldx][oldy]);
5034
5035   RemoveField(oldx, oldy);
5036   RemoveField(newx, newy);
5037
5038   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5039
5040   if (next_element != EL_UNDEFINED)
5041     Feld[oldx][oldy] = next_element;
5042
5043   TEST_DrawLevelField(oldx, oldy);
5044   TEST_DrawLevelField(newx, newy);
5045 }
5046
5047 void DrawDynamite(int x, int y)
5048 {
5049   int sx = SCREENX(x), sy = SCREENY(y);
5050   int graphic = el2img(Feld[x][y]);
5051   int frame;
5052
5053   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5054     return;
5055
5056   if (IS_WALKABLE_INSIDE(Back[x][y]))
5057     return;
5058
5059   if (Back[x][y])
5060     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5061   else if (Store[x][y])
5062     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5063
5064   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5065
5066   if (Back[x][y] || Store[x][y])
5067     DrawGraphicThruMask(sx, sy, graphic, frame);
5068   else
5069     DrawGraphic(sx, sy, graphic, frame);
5070 }
5071
5072 void CheckDynamite(int x, int y)
5073 {
5074   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5075   {
5076     MovDelay[x][y]--;
5077
5078     if (MovDelay[x][y] != 0)
5079     {
5080       DrawDynamite(x, y);
5081       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5082
5083       return;
5084     }
5085   }
5086
5087   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5088
5089   Bang(x, y);
5090 }
5091
5092 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5093 {
5094   boolean num_checked_players = 0;
5095   int i;
5096
5097   for (i = 0; i < MAX_PLAYERS; i++)
5098   {
5099     if (stored_player[i].active)
5100     {
5101       int sx = stored_player[i].jx;
5102       int sy = stored_player[i].jy;
5103
5104       if (num_checked_players == 0)
5105       {
5106         *sx1 = *sx2 = sx;
5107         *sy1 = *sy2 = sy;
5108       }
5109       else
5110       {
5111         *sx1 = MIN(*sx1, sx);
5112         *sy1 = MIN(*sy1, sy);
5113         *sx2 = MAX(*sx2, sx);
5114         *sy2 = MAX(*sy2, sy);
5115       }
5116
5117       num_checked_players++;
5118     }
5119   }
5120 }
5121
5122 static boolean checkIfAllPlayersFitToScreen_RND()
5123 {
5124   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5125
5126   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5127
5128   return (sx2 - sx1 < SCR_FIELDX &&
5129           sy2 - sy1 < SCR_FIELDY);
5130 }
5131
5132 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5133 {
5134   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5135
5136   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5137
5138   *sx = (sx1 + sx2) / 2;
5139   *sy = (sy1 + sy2) / 2;
5140 }
5141
5142 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5143                         boolean center_screen, boolean quick_relocation)
5144 {
5145   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5146   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5147   boolean no_delay = (tape.warp_forward);
5148   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5149   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5150   int new_scroll_x, new_scroll_y;
5151
5152   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5153   {
5154     /* case 1: quick relocation inside visible screen (without scrolling) */
5155
5156     RedrawPlayfield();
5157
5158     return;
5159   }
5160
5161   if (!level.shifted_relocation || center_screen)
5162   {
5163     /* relocation _with_ centering of screen */
5164
5165     new_scroll_x = SCROLL_POSITION_X(x);
5166     new_scroll_y = SCROLL_POSITION_Y(y);
5167   }
5168   else
5169   {
5170     /* relocation _without_ centering of screen */
5171
5172     int center_scroll_x = SCROLL_POSITION_X(old_x);
5173     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5174     int offset_x = x + (scroll_x - center_scroll_x);
5175     int offset_y = y + (scroll_y - center_scroll_y);
5176
5177     /* for new screen position, apply previous offset to center position */
5178     new_scroll_x = SCROLL_POSITION_X(offset_x);
5179     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5180   }
5181
5182   if (quick_relocation)
5183   {
5184     /* case 2: quick relocation (redraw without visible scrolling) */
5185
5186     scroll_x = new_scroll_x;
5187     scroll_y = new_scroll_y;
5188
5189     RedrawPlayfield();
5190
5191     return;
5192   }
5193
5194   /* case 3: visible relocation (with scrolling to new position) */
5195
5196   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5197
5198   SetVideoFrameDelay(wait_delay_value);
5199
5200   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5201   {
5202     int dx = 0, dy = 0;
5203     int fx = FX, fy = FY;
5204
5205     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5206     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5207
5208     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5209       break;
5210
5211     scroll_x -= dx;
5212     scroll_y -= dy;
5213
5214     fx += dx * TILEX / 2;
5215     fy += dy * TILEY / 2;
5216
5217     ScrollLevel(dx, dy);
5218     DrawAllPlayers();
5219
5220     /* scroll in two steps of half tile size to make things smoother */
5221     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5222
5223     /* scroll second step to align at full tile size */
5224     BlitScreenToBitmap(window);
5225   }
5226
5227   DrawAllPlayers();
5228   BackToFront();
5229
5230   SetVideoFrameDelay(frame_delay_value_old);
5231 }
5232
5233 void RelocatePlayer(int jx, int jy, int el_player_raw)
5234 {
5235   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5236   int player_nr = GET_PLAYER_NR(el_player);
5237   struct PlayerInfo *player = &stored_player[player_nr];
5238   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5239   boolean no_delay = (tape.warp_forward);
5240   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5241   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5242   int old_jx = player->jx;
5243   int old_jy = player->jy;
5244   int old_element = Feld[old_jx][old_jy];
5245   int element = Feld[jx][jy];
5246   boolean player_relocated = (old_jx != jx || old_jy != jy);
5247
5248   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5249   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5250   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5251   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5252   int leave_side_horiz = move_dir_horiz;
5253   int leave_side_vert  = move_dir_vert;
5254   int enter_side = enter_side_horiz | enter_side_vert;
5255   int leave_side = leave_side_horiz | leave_side_vert;
5256
5257   if (player->GameOver)         /* do not reanimate dead player */
5258     return;
5259
5260   if (!player_relocated)        /* no need to relocate the player */
5261     return;
5262
5263   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5264   {
5265     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5266     DrawLevelField(jx, jy);
5267   }
5268
5269   if (player->present)
5270   {
5271     while (player->MovPos)
5272     {
5273       ScrollPlayer(player, SCROLL_GO_ON);
5274       ScrollScreen(NULL, SCROLL_GO_ON);
5275
5276       AdvanceFrameAndPlayerCounters(player->index_nr);
5277
5278       DrawPlayer(player);
5279
5280       BackToFront_WithFrameDelay(wait_delay_value);
5281     }
5282
5283     DrawPlayer(player);         /* needed here only to cleanup last field */
5284     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5285
5286     player->is_moving = FALSE;
5287   }
5288
5289   if (IS_CUSTOM_ELEMENT(old_element))
5290     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5291                                CE_LEFT_BY_PLAYER,
5292                                player->index_bit, leave_side);
5293
5294   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5295                                       CE_PLAYER_LEAVES_X,
5296                                       player->index_bit, leave_side);
5297
5298   Feld[jx][jy] = el_player;
5299   InitPlayerField(jx, jy, el_player, TRUE);
5300
5301   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5302      possible that the relocation target field did not contain a player element,
5303      but a walkable element, to which the new player was relocated -- in this
5304      case, restore that (already initialized!) element on the player field */
5305   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5306   {
5307     Feld[jx][jy] = element;     /* restore previously existing element */
5308   }
5309
5310   /* only visually relocate centered player */
5311   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5312                      FALSE, level.instant_relocation);
5313
5314   TestIfPlayerTouchesBadThing(jx, jy);
5315   TestIfPlayerTouchesCustomElement(jx, jy);
5316
5317   if (IS_CUSTOM_ELEMENT(element))
5318     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5319                                player->index_bit, enter_side);
5320
5321   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5322                                       player->index_bit, enter_side);
5323
5324   if (player->is_switching)
5325   {
5326     /* ensure that relocation while still switching an element does not cause
5327        a new element to be treated as also switched directly after relocation
5328        (this is important for teleporter switches that teleport the player to
5329        a place where another teleporter switch is in the same direction, which
5330        would then incorrectly be treated as immediately switched before the
5331        direction key that caused the switch was released) */
5332
5333     player->switch_x += jx - old_jx;
5334     player->switch_y += jy - old_jy;
5335   }
5336 }
5337
5338 void Explode(int ex, int ey, int phase, int mode)
5339 {
5340   int x, y;
5341   int last_phase;
5342   int border_element;
5343
5344   /* !!! eliminate this variable !!! */
5345   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5346
5347   if (game.explosions_delayed)
5348   {
5349     ExplodeField[ex][ey] = mode;
5350     return;
5351   }
5352
5353   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5354   {
5355     int center_element = Feld[ex][ey];
5356     int artwork_element, explosion_element;     /* set these values later */
5357
5358     /* remove things displayed in background while burning dynamite */
5359     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5360       Back[ex][ey] = 0;
5361
5362     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5363     {
5364       /* put moving element to center field (and let it explode there) */
5365       center_element = MovingOrBlocked2Element(ex, ey);
5366       RemoveMovingField(ex, ey);
5367       Feld[ex][ey] = center_element;
5368     }
5369
5370     /* now "center_element" is finally determined -- set related values now */
5371     artwork_element = center_element;           /* for custom player artwork */
5372     explosion_element = center_element;         /* for custom player artwork */
5373
5374     if (IS_PLAYER(ex, ey))
5375     {
5376       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5377
5378       artwork_element = stored_player[player_nr].artwork_element;
5379
5380       if (level.use_explosion_element[player_nr])
5381       {
5382         explosion_element = level.explosion_element[player_nr];
5383         artwork_element = explosion_element;
5384       }
5385     }
5386
5387     if (mode == EX_TYPE_NORMAL ||
5388         mode == EX_TYPE_CENTER ||
5389         mode == EX_TYPE_CROSS)
5390       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5391
5392     last_phase = element_info[explosion_element].explosion_delay + 1;
5393
5394     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5395     {
5396       int xx = x - ex + 1;
5397       int yy = y - ey + 1;
5398       int element;
5399
5400       if (!IN_LEV_FIELD(x, y) ||
5401           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5402           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5403         continue;
5404
5405       element = Feld[x][y];
5406
5407       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5408       {
5409         element = MovingOrBlocked2Element(x, y);
5410
5411         if (!IS_EXPLOSION_PROOF(element))
5412           RemoveMovingField(x, y);
5413       }
5414
5415       /* indestructible elements can only explode in center (but not flames) */
5416       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5417                                            mode == EX_TYPE_BORDER)) ||
5418           element == EL_FLAMES)
5419         continue;
5420
5421       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5422          behaviour, for example when touching a yamyam that explodes to rocks
5423          with active deadly shield, a rock is created under the player !!! */
5424       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5425 #if 0
5426       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5427           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5428            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5429 #else
5430       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5431 #endif
5432       {
5433         if (IS_ACTIVE_BOMB(element))
5434         {
5435           /* re-activate things under the bomb like gate or penguin */
5436           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5437           Back[x][y] = 0;
5438         }
5439
5440         continue;
5441       }
5442
5443       /* save walkable background elements while explosion on same tile */
5444       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5445           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5446         Back[x][y] = element;
5447
5448       /* ignite explodable elements reached by other explosion */
5449       if (element == EL_EXPLOSION)
5450         element = Store2[x][y];
5451
5452       if (AmoebaNr[x][y] &&
5453           (element == EL_AMOEBA_FULL ||
5454            element == EL_BD_AMOEBA ||
5455            element == EL_AMOEBA_GROWING))
5456       {
5457         AmoebaCnt[AmoebaNr[x][y]]--;
5458         AmoebaCnt2[AmoebaNr[x][y]]--;
5459       }
5460
5461       RemoveField(x, y);
5462
5463       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5464       {
5465         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5466
5467         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5468
5469         if (PLAYERINFO(ex, ey)->use_murphy)
5470           Store[x][y] = EL_EMPTY;
5471       }
5472
5473       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5474          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5475       else if (ELEM_IS_PLAYER(center_element))
5476         Store[x][y] = EL_EMPTY;
5477       else if (center_element == EL_YAMYAM)
5478         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5479       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5480         Store[x][y] = element_info[center_element].content.e[xx][yy];
5481 #if 1
5482       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5483          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5484          otherwise) -- FIX THIS !!! */
5485       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5486         Store[x][y] = element_info[element].content.e[1][1];
5487 #else
5488       else if (!CAN_EXPLODE(element))
5489         Store[x][y] = element_info[element].content.e[1][1];
5490 #endif
5491       else
5492         Store[x][y] = EL_EMPTY;
5493
5494       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5495           center_element == EL_AMOEBA_TO_DIAMOND)
5496         Store2[x][y] = element;
5497
5498       Feld[x][y] = EL_EXPLOSION;
5499       GfxElement[x][y] = artwork_element;
5500
5501       ExplodePhase[x][y] = 1;
5502       ExplodeDelay[x][y] = last_phase;
5503
5504       Stop[x][y] = TRUE;
5505     }
5506
5507     if (center_element == EL_YAMYAM)
5508       game.yamyam_content_nr =
5509         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5510
5511     return;
5512   }
5513
5514   if (Stop[ex][ey])
5515     return;
5516
5517   x = ex;
5518   y = ey;
5519
5520   if (phase == 1)
5521     GfxFrame[x][y] = 0;         /* restart explosion animation */
5522
5523   last_phase = ExplodeDelay[x][y];
5524
5525   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5526
5527   /* this can happen if the player leaves an explosion just in time */
5528   if (GfxElement[x][y] == EL_UNDEFINED)
5529     GfxElement[x][y] = EL_EMPTY;
5530
5531   border_element = Store2[x][y];
5532   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5533     border_element = StorePlayer[x][y];
5534
5535   if (phase == element_info[border_element].ignition_delay ||
5536       phase == last_phase)
5537   {
5538     boolean border_explosion = FALSE;
5539
5540     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5541         !PLAYER_EXPLOSION_PROTECTED(x, y))
5542     {
5543       KillPlayerUnlessExplosionProtected(x, y);
5544       border_explosion = TRUE;
5545     }
5546     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5547     {
5548       Feld[x][y] = Store2[x][y];
5549       Store2[x][y] = 0;
5550       Bang(x, y);
5551       border_explosion = TRUE;
5552     }
5553     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5554     {
5555       AmoebeUmwandeln(x, y);
5556       Store2[x][y] = 0;
5557       border_explosion = TRUE;
5558     }
5559
5560     /* if an element just explodes due to another explosion (chain-reaction),
5561        do not immediately end the new explosion when it was the last frame of
5562        the explosion (as it would be done in the following "if"-statement!) */
5563     if (border_explosion && phase == last_phase)
5564       return;
5565   }
5566
5567   if (phase == last_phase)
5568   {
5569     int element;
5570
5571     element = Feld[x][y] = Store[x][y];
5572     Store[x][y] = Store2[x][y] = 0;
5573     GfxElement[x][y] = EL_UNDEFINED;
5574
5575     /* player can escape from explosions and might therefore be still alive */
5576     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5577         element <= EL_PLAYER_IS_EXPLODING_4)
5578     {
5579       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5580       int explosion_element = EL_PLAYER_1 + player_nr;
5581       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5582       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5583
5584       if (level.use_explosion_element[player_nr])
5585         explosion_element = level.explosion_element[player_nr];
5586
5587       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5588                     element_info[explosion_element].content.e[xx][yy]);
5589     }
5590
5591     /* restore probably existing indestructible background element */
5592     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5593       element = Feld[x][y] = Back[x][y];
5594     Back[x][y] = 0;
5595
5596     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5597     GfxDir[x][y] = MV_NONE;
5598     ChangeDelay[x][y] = 0;
5599     ChangePage[x][y] = -1;
5600
5601     CustomValue[x][y] = 0;
5602
5603     InitField_WithBug2(x, y, FALSE);
5604
5605     TEST_DrawLevelField(x, y);
5606
5607     TestIfElementTouchesCustomElement(x, y);
5608
5609     if (GFX_CRUMBLED(element))
5610       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5611
5612     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5613       StorePlayer[x][y] = 0;
5614
5615     if (ELEM_IS_PLAYER(element))
5616       RelocatePlayer(x, y, element);
5617   }
5618   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5619   {
5620     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5621     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5622
5623     if (phase == delay)
5624       TEST_DrawLevelFieldCrumbled(x, y);
5625
5626     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5627     {
5628       DrawLevelElement(x, y, Back[x][y]);
5629       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5630     }
5631     else if (IS_WALKABLE_UNDER(Back[x][y]))
5632     {
5633       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5634       DrawLevelElementThruMask(x, y, Back[x][y]);
5635     }
5636     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5637       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5638   }
5639 }
5640
5641 void DynaExplode(int ex, int ey)
5642 {
5643   int i, j;
5644   int dynabomb_element = Feld[ex][ey];
5645   int dynabomb_size = 1;
5646   boolean dynabomb_xl = FALSE;
5647   struct PlayerInfo *player;
5648   static int xy[4][2] =
5649   {
5650     { 0, -1 },
5651     { -1, 0 },
5652     { +1, 0 },
5653     { 0, +1 }
5654   };
5655
5656   if (IS_ACTIVE_BOMB(dynabomb_element))
5657   {
5658     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5659     dynabomb_size = player->dynabomb_size;
5660     dynabomb_xl = player->dynabomb_xl;
5661     player->dynabombs_left++;
5662   }
5663
5664   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5665
5666   for (i = 0; i < NUM_DIRECTIONS; i++)
5667   {
5668     for (j = 1; j <= dynabomb_size; j++)
5669     {
5670       int x = ex + j * xy[i][0];
5671       int y = ey + j * xy[i][1];
5672       int element;
5673
5674       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5675         break;
5676
5677       element = Feld[x][y];
5678
5679       /* do not restart explosions of fields with active bombs */
5680       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5681         continue;
5682
5683       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5684
5685       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5686           !IS_DIGGABLE(element) && !dynabomb_xl)
5687         break;
5688     }
5689   }
5690 }
5691
5692 void Bang(int x, int y)
5693 {
5694   int element = MovingOrBlocked2Element(x, y);
5695   int explosion_type = EX_TYPE_NORMAL;
5696
5697   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5698   {
5699     struct PlayerInfo *player = PLAYERINFO(x, y);
5700
5701     element = Feld[x][y] = player->initial_element;
5702
5703     if (level.use_explosion_element[player->index_nr])
5704     {
5705       int explosion_element = level.explosion_element[player->index_nr];
5706
5707       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5708         explosion_type = EX_TYPE_CROSS;
5709       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5710         explosion_type = EX_TYPE_CENTER;
5711     }
5712   }
5713
5714   switch (element)
5715   {
5716     case EL_BUG:
5717     case EL_SPACESHIP:
5718     case EL_BD_BUTTERFLY:
5719     case EL_BD_FIREFLY:
5720     case EL_YAMYAM:
5721     case EL_DARK_YAMYAM:
5722     case EL_ROBOT:
5723     case EL_PACMAN:
5724     case EL_MOLE:
5725       RaiseScoreElement(element);
5726       break;
5727
5728     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5729     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5730     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5731     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5732     case EL_DYNABOMB_INCREASE_NUMBER:
5733     case EL_DYNABOMB_INCREASE_SIZE:
5734     case EL_DYNABOMB_INCREASE_POWER:
5735       explosion_type = EX_TYPE_DYNA;
5736       break;
5737
5738     case EL_DC_LANDMINE:
5739       explosion_type = EX_TYPE_CENTER;
5740       break;
5741
5742     case EL_PENGUIN:
5743     case EL_LAMP:
5744     case EL_LAMP_ACTIVE:
5745     case EL_AMOEBA_TO_DIAMOND:
5746       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5747         explosion_type = EX_TYPE_CENTER;
5748       break;
5749
5750     default:
5751       if (element_info[element].explosion_type == EXPLODES_CROSS)
5752         explosion_type = EX_TYPE_CROSS;
5753       else if (element_info[element].explosion_type == EXPLODES_1X1)
5754         explosion_type = EX_TYPE_CENTER;
5755       break;
5756   }
5757
5758   if (explosion_type == EX_TYPE_DYNA)
5759     DynaExplode(x, y);
5760   else
5761     Explode(x, y, EX_PHASE_START, explosion_type);
5762
5763   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5764 }
5765
5766 void SplashAcid(int x, int y)
5767 {
5768   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5769       (!IN_LEV_FIELD(x - 1, y - 2) ||
5770        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5771     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5772
5773   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5774       (!IN_LEV_FIELD(x + 1, y - 2) ||
5775        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5776     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5777
5778   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5779 }
5780
5781 static void InitBeltMovement()
5782 {
5783   static int belt_base_element[4] =
5784   {
5785     EL_CONVEYOR_BELT_1_LEFT,
5786     EL_CONVEYOR_BELT_2_LEFT,
5787     EL_CONVEYOR_BELT_3_LEFT,
5788     EL_CONVEYOR_BELT_4_LEFT
5789   };
5790   static int belt_base_active_element[4] =
5791   {
5792     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5793     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5794     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5795     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5796   };
5797
5798   int x, y, i, j;
5799
5800   /* set frame order for belt animation graphic according to belt direction */
5801   for (i = 0; i < NUM_BELTS; i++)
5802   {
5803     int belt_nr = i;
5804
5805     for (j = 0; j < NUM_BELT_PARTS; j++)
5806     {
5807       int element = belt_base_active_element[belt_nr] + j;
5808       int graphic_1 = el2img(element);
5809       int graphic_2 = el2panelimg(element);
5810
5811       if (game.belt_dir[i] == MV_LEFT)
5812       {
5813         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5814         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5815       }
5816       else
5817       {
5818         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5819         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5820       }
5821     }
5822   }
5823
5824   SCAN_PLAYFIELD(x, y)
5825   {
5826     int element = Feld[x][y];
5827
5828     for (i = 0; i < NUM_BELTS; i++)
5829     {
5830       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5831       {
5832         int e_belt_nr = getBeltNrFromBeltElement(element);
5833         int belt_nr = i;
5834
5835         if (e_belt_nr == belt_nr)
5836         {
5837           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5838
5839           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5840         }
5841       }
5842     }
5843   }
5844 }
5845
5846 static void ToggleBeltSwitch(int x, int y)
5847 {
5848   static int belt_base_element[4] =
5849   {
5850     EL_CONVEYOR_BELT_1_LEFT,
5851     EL_CONVEYOR_BELT_2_LEFT,
5852     EL_CONVEYOR_BELT_3_LEFT,
5853     EL_CONVEYOR_BELT_4_LEFT
5854   };
5855   static int belt_base_active_element[4] =
5856   {
5857     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5858     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5859     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5860     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5861   };
5862   static int belt_base_switch_element[4] =
5863   {
5864     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5865     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5866     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5867     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5868   };
5869   static int belt_move_dir[4] =
5870   {
5871     MV_LEFT,
5872     MV_NONE,
5873     MV_RIGHT,
5874     MV_NONE,
5875   };
5876
5877   int element = Feld[x][y];
5878   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5879   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5880   int belt_dir = belt_move_dir[belt_dir_nr];
5881   int xx, yy, i;
5882
5883   if (!IS_BELT_SWITCH(element))
5884     return;
5885
5886   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5887   game.belt_dir[belt_nr] = belt_dir;
5888
5889   if (belt_dir_nr == 3)
5890     belt_dir_nr = 1;
5891
5892   /* set frame order for belt animation graphic according to belt direction */
5893   for (i = 0; i < NUM_BELT_PARTS; i++)
5894   {
5895     int element = belt_base_active_element[belt_nr] + i;
5896     int graphic_1 = el2img(element);
5897     int graphic_2 = el2panelimg(element);
5898
5899     if (belt_dir == MV_LEFT)
5900     {
5901       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5903     }
5904     else
5905     {
5906       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5907       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5908     }
5909   }
5910
5911   SCAN_PLAYFIELD(xx, yy)
5912   {
5913     int element = Feld[xx][yy];
5914
5915     if (IS_BELT_SWITCH(element))
5916     {
5917       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5918
5919       if (e_belt_nr == belt_nr)
5920       {
5921         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5922         TEST_DrawLevelField(xx, yy);
5923       }
5924     }
5925     else if (IS_BELT(element) && belt_dir != MV_NONE)
5926     {
5927       int e_belt_nr = getBeltNrFromBeltElement(element);
5928
5929       if (e_belt_nr == belt_nr)
5930       {
5931         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5932
5933         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5934         TEST_DrawLevelField(xx, yy);
5935       }
5936     }
5937     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5938     {
5939       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5940
5941       if (e_belt_nr == belt_nr)
5942       {
5943         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5944
5945         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5946         TEST_DrawLevelField(xx, yy);
5947       }
5948     }
5949   }
5950 }
5951
5952 static void ToggleSwitchgateSwitch(int x, int y)
5953 {
5954   int xx, yy;
5955
5956   game.switchgate_pos = !game.switchgate_pos;
5957
5958   SCAN_PLAYFIELD(xx, yy)
5959   {
5960     int element = Feld[xx][yy];
5961
5962     if (element == EL_SWITCHGATE_SWITCH_UP)
5963     {
5964       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5965       TEST_DrawLevelField(xx, yy);
5966     }
5967     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5968     {
5969       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5970       TEST_DrawLevelField(xx, yy);
5971     }
5972     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5973     {
5974       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5975       TEST_DrawLevelField(xx, yy);
5976     }
5977     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5978     {
5979       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5980       TEST_DrawLevelField(xx, yy);
5981     }
5982     else if (element == EL_SWITCHGATE_OPEN ||
5983              element == EL_SWITCHGATE_OPENING)
5984     {
5985       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5986
5987       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5988     }
5989     else if (element == EL_SWITCHGATE_CLOSED ||
5990              element == EL_SWITCHGATE_CLOSING)
5991     {
5992       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5993
5994       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5995     }
5996   }
5997 }
5998
5999 static int getInvisibleActiveFromInvisibleElement(int element)
6000 {
6001   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6002           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6003           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6004           element);
6005 }
6006
6007 static int getInvisibleFromInvisibleActiveElement(int element)
6008 {
6009   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6010           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6011           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6012           element);
6013 }
6014
6015 static void RedrawAllLightSwitchesAndInvisibleElements()
6016 {
6017   int x, y;
6018
6019   SCAN_PLAYFIELD(x, y)
6020   {
6021     int element = Feld[x][y];
6022
6023     if (element == EL_LIGHT_SWITCH &&
6024         game.light_time_left > 0)
6025     {
6026       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6027       TEST_DrawLevelField(x, y);
6028     }
6029     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6030              game.light_time_left == 0)
6031     {
6032       Feld[x][y] = EL_LIGHT_SWITCH;
6033       TEST_DrawLevelField(x, y);
6034     }
6035     else if (element == EL_EMC_DRIPPER &&
6036              game.light_time_left > 0)
6037     {
6038       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6039       TEST_DrawLevelField(x, y);
6040     }
6041     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6042              game.light_time_left == 0)
6043     {
6044       Feld[x][y] = EL_EMC_DRIPPER;
6045       TEST_DrawLevelField(x, y);
6046     }
6047     else if (element == EL_INVISIBLE_STEELWALL ||
6048              element == EL_INVISIBLE_WALL ||
6049              element == EL_INVISIBLE_SAND)
6050     {
6051       if (game.light_time_left > 0)
6052         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6053
6054       TEST_DrawLevelField(x, y);
6055
6056       /* uncrumble neighbour fields, if needed */
6057       if (element == EL_INVISIBLE_SAND)
6058         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6059     }
6060     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6061              element == EL_INVISIBLE_WALL_ACTIVE ||
6062              element == EL_INVISIBLE_SAND_ACTIVE)
6063     {
6064       if (game.light_time_left == 0)
6065         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6066
6067       TEST_DrawLevelField(x, y);
6068
6069       /* re-crumble neighbour fields, if needed */
6070       if (element == EL_INVISIBLE_SAND)
6071         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6072     }
6073   }
6074 }
6075
6076 static void RedrawAllInvisibleElementsForLenses()
6077 {
6078   int x, y;
6079
6080   SCAN_PLAYFIELD(x, y)
6081   {
6082     int element = Feld[x][y];
6083
6084     if (element == EL_EMC_DRIPPER &&
6085         game.lenses_time_left > 0)
6086     {
6087       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6088       TEST_DrawLevelField(x, y);
6089     }
6090     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6091              game.lenses_time_left == 0)
6092     {
6093       Feld[x][y] = EL_EMC_DRIPPER;
6094       TEST_DrawLevelField(x, y);
6095     }
6096     else if (element == EL_INVISIBLE_STEELWALL ||
6097              element == EL_INVISIBLE_WALL ||
6098              element == EL_INVISIBLE_SAND)
6099     {
6100       if (game.lenses_time_left > 0)
6101         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6102
6103       TEST_DrawLevelField(x, y);
6104
6105       /* uncrumble neighbour fields, if needed */
6106       if (element == EL_INVISIBLE_SAND)
6107         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6108     }
6109     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6110              element == EL_INVISIBLE_WALL_ACTIVE ||
6111              element == EL_INVISIBLE_SAND_ACTIVE)
6112     {
6113       if (game.lenses_time_left == 0)
6114         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6115
6116       TEST_DrawLevelField(x, y);
6117
6118       /* re-crumble neighbour fields, if needed */
6119       if (element == EL_INVISIBLE_SAND)
6120         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6121     }
6122   }
6123 }
6124
6125 static void RedrawAllInvisibleElementsForMagnifier()
6126 {
6127   int x, y;
6128
6129   SCAN_PLAYFIELD(x, y)
6130   {
6131     int element = Feld[x][y];
6132
6133     if (element == EL_EMC_FAKE_GRASS &&
6134         game.magnify_time_left > 0)
6135     {
6136       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6137       TEST_DrawLevelField(x, y);
6138     }
6139     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6140              game.magnify_time_left == 0)
6141     {
6142       Feld[x][y] = EL_EMC_FAKE_GRASS;
6143       TEST_DrawLevelField(x, y);
6144     }
6145     else if (IS_GATE_GRAY(element) &&
6146              game.magnify_time_left > 0)
6147     {
6148       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6149                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6150                     IS_EM_GATE_GRAY(element) ?
6151                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6152                     IS_EMC_GATE_GRAY(element) ?
6153                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6154                     IS_DC_GATE_GRAY(element) ?
6155                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6156                     element);
6157       TEST_DrawLevelField(x, y);
6158     }
6159     else if (IS_GATE_GRAY_ACTIVE(element) &&
6160              game.magnify_time_left == 0)
6161     {
6162       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6163                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6164                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6165                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6166                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6167                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6168                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6169                     EL_DC_GATE_WHITE_GRAY :
6170                     element);
6171       TEST_DrawLevelField(x, y);
6172     }
6173   }
6174 }
6175
6176 static void ToggleLightSwitch(int x, int y)
6177 {
6178   int element = Feld[x][y];
6179
6180   game.light_time_left =
6181     (element == EL_LIGHT_SWITCH ?
6182      level.time_light * FRAMES_PER_SECOND : 0);
6183
6184   RedrawAllLightSwitchesAndInvisibleElements();
6185 }
6186
6187 static void ActivateTimegateSwitch(int x, int y)
6188 {
6189   int xx, yy;
6190
6191   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6192
6193   SCAN_PLAYFIELD(xx, yy)
6194   {
6195     int element = Feld[xx][yy];
6196
6197     if (element == EL_TIMEGATE_CLOSED ||
6198         element == EL_TIMEGATE_CLOSING)
6199     {
6200       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6201       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6202     }
6203
6204     /*
6205     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6206     {
6207       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6208       TEST_DrawLevelField(xx, yy);
6209     }
6210     */
6211
6212   }
6213
6214   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6215                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6216 }
6217
6218 void Impact(int x, int y)
6219 {
6220   boolean last_line = (y == lev_fieldy - 1);
6221   boolean object_hit = FALSE;
6222   boolean impact = (last_line || object_hit);
6223   int element = Feld[x][y];
6224   int smashed = EL_STEELWALL;
6225
6226   if (!last_line)       /* check if element below was hit */
6227   {
6228     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6229       return;
6230
6231     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6232                                          MovDir[x][y + 1] != MV_DOWN ||
6233                                          MovPos[x][y + 1] <= TILEY / 2));
6234
6235     /* do not smash moving elements that left the smashed field in time */
6236     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6237         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6238       object_hit = FALSE;
6239
6240 #if USE_QUICKSAND_IMPACT_BUGFIX
6241     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6242     {
6243       RemoveMovingField(x, y + 1);
6244       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6245       Feld[x][y + 2] = EL_ROCK;
6246       TEST_DrawLevelField(x, y + 2);
6247
6248       object_hit = TRUE;
6249     }
6250
6251     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6252     {
6253       RemoveMovingField(x, y + 1);
6254       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6255       Feld[x][y + 2] = EL_ROCK;
6256       TEST_DrawLevelField(x, y + 2);
6257
6258       object_hit = TRUE;
6259     }
6260 #endif
6261
6262     if (object_hit)
6263       smashed = MovingOrBlocked2Element(x, y + 1);
6264
6265     impact = (last_line || object_hit);
6266   }
6267
6268   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6269   {
6270     SplashAcid(x, y + 1);
6271     return;
6272   }
6273
6274   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6275   /* only reset graphic animation if graphic really changes after impact */
6276   if (impact &&
6277       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6278   {
6279     ResetGfxAnimation(x, y);
6280     TEST_DrawLevelField(x, y);
6281   }
6282
6283   if (impact && CAN_EXPLODE_IMPACT(element))
6284   {
6285     Bang(x, y);
6286     return;
6287   }
6288   else if (impact && element == EL_PEARL &&
6289            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6290   {
6291     ResetGfxAnimation(x, y);
6292
6293     Feld[x][y] = EL_PEARL_BREAKING;
6294     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6295     return;
6296   }
6297   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6298   {
6299     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6300
6301     return;
6302   }
6303
6304   if (impact && element == EL_AMOEBA_DROP)
6305   {
6306     if (object_hit && IS_PLAYER(x, y + 1))
6307       KillPlayerUnlessEnemyProtected(x, y + 1);
6308     else if (object_hit && smashed == EL_PENGUIN)
6309       Bang(x, y + 1);
6310     else
6311     {
6312       Feld[x][y] = EL_AMOEBA_GROWING;
6313       Store[x][y] = EL_AMOEBA_WET;
6314
6315       ResetRandomAnimationValue(x, y);
6316     }
6317     return;
6318   }
6319
6320   if (object_hit)               /* check which object was hit */
6321   {
6322     if ((CAN_PASS_MAGIC_WALL(element) && 
6323          (smashed == EL_MAGIC_WALL ||
6324           smashed == EL_BD_MAGIC_WALL)) ||
6325         (CAN_PASS_DC_MAGIC_WALL(element) &&
6326          smashed == EL_DC_MAGIC_WALL))
6327     {
6328       int xx, yy;
6329       int activated_magic_wall =
6330         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6331          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6332          EL_DC_MAGIC_WALL_ACTIVE);
6333
6334       /* activate magic wall / mill */
6335       SCAN_PLAYFIELD(xx, yy)
6336       {
6337         if (Feld[xx][yy] == smashed)
6338           Feld[xx][yy] = activated_magic_wall;
6339       }
6340
6341       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6342       game.magic_wall_active = TRUE;
6343
6344       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6345                             SND_MAGIC_WALL_ACTIVATING :
6346                             smashed == EL_BD_MAGIC_WALL ?
6347                             SND_BD_MAGIC_WALL_ACTIVATING :
6348                             SND_DC_MAGIC_WALL_ACTIVATING));
6349     }
6350
6351     if (IS_PLAYER(x, y + 1))
6352     {
6353       if (CAN_SMASH_PLAYER(element))
6354       {
6355         KillPlayerUnlessEnemyProtected(x, y + 1);
6356         return;
6357       }
6358     }
6359     else if (smashed == EL_PENGUIN)
6360     {
6361       if (CAN_SMASH_PLAYER(element))
6362       {
6363         Bang(x, y + 1);
6364         return;
6365       }
6366     }
6367     else if (element == EL_BD_DIAMOND)
6368     {
6369       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6370       {
6371         Bang(x, y + 1);
6372         return;
6373       }
6374     }
6375     else if (((element == EL_SP_INFOTRON ||
6376                element == EL_SP_ZONK) &&
6377               (smashed == EL_SP_SNIKSNAK ||
6378                smashed == EL_SP_ELECTRON ||
6379                smashed == EL_SP_DISK_ORANGE)) ||
6380              (element == EL_SP_INFOTRON &&
6381               smashed == EL_SP_DISK_YELLOW))
6382     {
6383       Bang(x, y + 1);
6384       return;
6385     }
6386     else if (CAN_SMASH_EVERYTHING(element))
6387     {
6388       if (IS_CLASSIC_ENEMY(smashed) ||
6389           CAN_EXPLODE_SMASHED(smashed))
6390       {
6391         Bang(x, y + 1);
6392         return;
6393       }
6394       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6395       {
6396         if (smashed == EL_LAMP ||
6397             smashed == EL_LAMP_ACTIVE)
6398         {
6399           Bang(x, y + 1);
6400           return;
6401         }
6402         else if (smashed == EL_NUT)
6403         {
6404           Feld[x][y + 1] = EL_NUT_BREAKING;
6405           PlayLevelSound(x, y, SND_NUT_BREAKING);
6406           RaiseScoreElement(EL_NUT);
6407           return;
6408         }
6409         else if (smashed == EL_PEARL)
6410         {
6411           ResetGfxAnimation(x, y);
6412
6413           Feld[x][y + 1] = EL_PEARL_BREAKING;
6414           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6415           return;
6416         }
6417         else if (smashed == EL_DIAMOND)
6418         {
6419           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6420           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6421           return;
6422         }
6423         else if (IS_BELT_SWITCH(smashed))
6424         {
6425           ToggleBeltSwitch(x, y + 1);
6426         }
6427         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6428                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6429                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6430                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6431         {
6432           ToggleSwitchgateSwitch(x, y + 1);
6433         }
6434         else if (smashed == EL_LIGHT_SWITCH ||
6435                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6436         {
6437           ToggleLightSwitch(x, y + 1);
6438         }
6439         else
6440         {
6441           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6442
6443           CheckElementChangeBySide(x, y + 1, smashed, element,
6444                                    CE_SWITCHED, CH_SIDE_TOP);
6445           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6446                                             CH_SIDE_TOP);
6447         }
6448       }
6449       else
6450       {
6451         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6452       }
6453     }
6454   }
6455
6456   /* play sound of magic wall / mill */
6457   if (!last_line &&
6458       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6459        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6460        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6461   {
6462     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6463       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6464     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6465       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6466     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6467       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6468
6469     return;
6470   }
6471
6472   /* play sound of object that hits the ground */
6473   if (last_line || object_hit)
6474     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6475 }
6476
6477 inline static void TurnRoundExt(int x, int y)
6478 {
6479   static struct
6480   {
6481     int dx, dy;
6482   } move_xy[] =
6483   {
6484     {  0,  0 },
6485     { -1,  0 },
6486     { +1,  0 },
6487     {  0,  0 },
6488     {  0, -1 },
6489     {  0,  0 }, { 0, 0 }, { 0, 0 },
6490     {  0, +1 }
6491   };
6492   static struct
6493   {
6494     int left, right, back;
6495   } turn[] =
6496   {
6497     { 0,        0,              0        },
6498     { MV_DOWN,  MV_UP,          MV_RIGHT },
6499     { MV_UP,    MV_DOWN,        MV_LEFT  },
6500     { 0,        0,              0        },
6501     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6502     { 0,        0,              0        },
6503     { 0,        0,              0        },
6504     { 0,        0,              0        },
6505     { MV_RIGHT, MV_LEFT,        MV_UP    }
6506   };
6507
6508   int element = Feld[x][y];
6509   int move_pattern = element_info[element].move_pattern;
6510
6511   int old_move_dir = MovDir[x][y];
6512   int left_dir  = turn[old_move_dir].left;
6513   int right_dir = turn[old_move_dir].right;
6514   int back_dir  = turn[old_move_dir].back;
6515
6516   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6517   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6518   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6519   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6520
6521   int left_x  = x + left_dx,  left_y  = y + left_dy;
6522   int right_x = x + right_dx, right_y = y + right_dy;
6523   int move_x  = x + move_dx,  move_y  = y + move_dy;
6524
6525   int xx, yy;
6526
6527   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6528   {
6529     TestIfBadThingTouchesOtherBadThing(x, y);
6530
6531     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6532       MovDir[x][y] = right_dir;
6533     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6534       MovDir[x][y] = left_dir;
6535
6536     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6537       MovDelay[x][y] = 9;
6538     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6539       MovDelay[x][y] = 1;
6540   }
6541   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6542   {
6543     TestIfBadThingTouchesOtherBadThing(x, y);
6544
6545     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6546       MovDir[x][y] = left_dir;
6547     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6548       MovDir[x][y] = right_dir;
6549
6550     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6551       MovDelay[x][y] = 9;
6552     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6553       MovDelay[x][y] = 1;
6554   }
6555   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6556   {
6557     TestIfBadThingTouchesOtherBadThing(x, y);
6558
6559     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6560       MovDir[x][y] = left_dir;
6561     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6562       MovDir[x][y] = right_dir;
6563
6564     if (MovDir[x][y] != old_move_dir)
6565       MovDelay[x][y] = 9;
6566   }
6567   else if (element == EL_YAMYAM)
6568   {
6569     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6570     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6571
6572     if (can_turn_left && can_turn_right)
6573       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6574     else if (can_turn_left)
6575       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6576     else if (can_turn_right)
6577       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6578     else
6579       MovDir[x][y] = back_dir;
6580
6581     MovDelay[x][y] = 16 + 16 * RND(3);
6582   }
6583   else if (element == EL_DARK_YAMYAM)
6584   {
6585     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6586                                                          left_x, left_y);
6587     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6588                                                          right_x, right_y);
6589
6590     if (can_turn_left && can_turn_right)
6591       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6592     else if (can_turn_left)
6593       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6594     else if (can_turn_right)
6595       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6596     else
6597       MovDir[x][y] = back_dir;
6598
6599     MovDelay[x][y] = 16 + 16 * RND(3);
6600   }
6601   else if (element == EL_PACMAN)
6602   {
6603     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6604     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6605
6606     if (can_turn_left && can_turn_right)
6607       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6608     else if (can_turn_left)
6609       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6610     else if (can_turn_right)
6611       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6612     else
6613       MovDir[x][y] = back_dir;
6614
6615     MovDelay[x][y] = 6 + RND(40);
6616   }
6617   else if (element == EL_PIG)
6618   {
6619     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6620     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6621     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6622     boolean should_turn_left, should_turn_right, should_move_on;
6623     int rnd_value = 24;
6624     int rnd = RND(rnd_value);
6625
6626     should_turn_left = (can_turn_left &&
6627                         (!can_move_on ||
6628                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6629                                                    y + back_dy + left_dy)));
6630     should_turn_right = (can_turn_right &&
6631                          (!can_move_on ||
6632                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6633                                                     y + back_dy + right_dy)));
6634     should_move_on = (can_move_on &&
6635                       (!can_turn_left ||
6636                        !can_turn_right ||
6637                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6638                                                  y + move_dy + left_dy) ||
6639                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6640                                                  y + move_dy + right_dy)));
6641
6642     if (should_turn_left || should_turn_right || should_move_on)
6643     {
6644       if (should_turn_left && should_turn_right && should_move_on)
6645         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6646                         rnd < 2 * rnd_value / 3 ? right_dir :
6647                         old_move_dir);
6648       else if (should_turn_left && should_turn_right)
6649         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6650       else if (should_turn_left && should_move_on)
6651         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6652       else if (should_turn_right && should_move_on)
6653         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6654       else if (should_turn_left)
6655         MovDir[x][y] = left_dir;
6656       else if (should_turn_right)
6657         MovDir[x][y] = right_dir;
6658       else if (should_move_on)
6659         MovDir[x][y] = old_move_dir;
6660     }
6661     else if (can_move_on && rnd > rnd_value / 8)
6662       MovDir[x][y] = old_move_dir;
6663     else if (can_turn_left && can_turn_right)
6664       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6665     else if (can_turn_left && rnd > rnd_value / 8)
6666       MovDir[x][y] = left_dir;
6667     else if (can_turn_right && rnd > rnd_value/8)
6668       MovDir[x][y] = right_dir;
6669     else
6670       MovDir[x][y] = back_dir;
6671
6672     xx = x + move_xy[MovDir[x][y]].dx;
6673     yy = y + move_xy[MovDir[x][y]].dy;
6674
6675     if (!IN_LEV_FIELD(xx, yy) ||
6676         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6677       MovDir[x][y] = old_move_dir;
6678
6679     MovDelay[x][y] = 0;
6680   }
6681   else if (element == EL_DRAGON)
6682   {
6683     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6684     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6685     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6686     int rnd_value = 24;
6687     int rnd = RND(rnd_value);
6688
6689     if (can_move_on && rnd > rnd_value / 8)
6690       MovDir[x][y] = old_move_dir;
6691     else if (can_turn_left && can_turn_right)
6692       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6693     else if (can_turn_left && rnd > rnd_value / 8)
6694       MovDir[x][y] = left_dir;
6695     else if (can_turn_right && rnd > rnd_value / 8)
6696       MovDir[x][y] = right_dir;
6697     else
6698       MovDir[x][y] = back_dir;
6699
6700     xx = x + move_xy[MovDir[x][y]].dx;
6701     yy = y + move_xy[MovDir[x][y]].dy;
6702
6703     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6704       MovDir[x][y] = old_move_dir;
6705
6706     MovDelay[x][y] = 0;
6707   }
6708   else if (element == EL_MOLE)
6709   {
6710     boolean can_move_on =
6711       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6712                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6713                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6714     if (!can_move_on)
6715     {
6716       boolean can_turn_left =
6717         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6718                               IS_AMOEBOID(Feld[left_x][left_y])));
6719
6720       boolean can_turn_right =
6721         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6722                               IS_AMOEBOID(Feld[right_x][right_y])));
6723
6724       if (can_turn_left && can_turn_right)
6725         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6726       else if (can_turn_left)
6727         MovDir[x][y] = left_dir;
6728       else
6729         MovDir[x][y] = right_dir;
6730     }
6731
6732     if (MovDir[x][y] != old_move_dir)
6733       MovDelay[x][y] = 9;
6734   }
6735   else if (element == EL_BALLOON)
6736   {
6737     MovDir[x][y] = game.wind_direction;
6738     MovDelay[x][y] = 0;
6739   }
6740   else if (element == EL_SPRING)
6741   {
6742     if (MovDir[x][y] & MV_HORIZONTAL)
6743     {
6744       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6745           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6746       {
6747         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6748         ResetGfxAnimation(move_x, move_y);
6749         TEST_DrawLevelField(move_x, move_y);
6750
6751         MovDir[x][y] = back_dir;
6752       }
6753       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6754                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6755         MovDir[x][y] = MV_NONE;
6756     }
6757
6758     MovDelay[x][y] = 0;
6759   }
6760   else if (element == EL_ROBOT ||
6761            element == EL_SATELLITE ||
6762            element == EL_PENGUIN ||
6763            element == EL_EMC_ANDROID)
6764   {
6765     int attr_x = -1, attr_y = -1;
6766
6767     if (AllPlayersGone)
6768     {
6769       attr_x = ExitX;
6770       attr_y = ExitY;
6771     }
6772     else
6773     {
6774       int i;
6775
6776       for (i = 0; i < MAX_PLAYERS; i++)
6777       {
6778         struct PlayerInfo *player = &stored_player[i];
6779         int jx = player->jx, jy = player->jy;
6780
6781         if (!player->active)
6782           continue;
6783
6784         if (attr_x == -1 ||
6785             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6786         {
6787           attr_x = jx;
6788           attr_y = jy;
6789         }
6790       }
6791     }
6792
6793     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6794         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6795          game.engine_version < VERSION_IDENT(3,1,0,0)))
6796     {
6797       attr_x = ZX;
6798       attr_y = ZY;
6799     }
6800
6801     if (element == EL_PENGUIN)
6802     {
6803       int i;
6804       static int xy[4][2] =
6805       {
6806         { 0, -1 },
6807         { -1, 0 },
6808         { +1, 0 },
6809         { 0, +1 }
6810       };
6811
6812       for (i = 0; i < NUM_DIRECTIONS; i++)
6813       {
6814         int ex = x + xy[i][0];
6815         int ey = y + xy[i][1];
6816
6817         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6818                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6819                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6820                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6821         {
6822           attr_x = ex;
6823           attr_y = ey;
6824           break;
6825         }
6826       }
6827     }
6828
6829     MovDir[x][y] = MV_NONE;
6830     if (attr_x < x)
6831       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6832     else if (attr_x > x)
6833       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6834     if (attr_y < y)
6835       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6836     else if (attr_y > y)
6837       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6838
6839     if (element == EL_ROBOT)
6840     {
6841       int newx, newy;
6842
6843       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6844         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6845       Moving2Blocked(x, y, &newx, &newy);
6846
6847       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6848         MovDelay[x][y] = 8 + 8 * !RND(3);
6849       else
6850         MovDelay[x][y] = 16;
6851     }
6852     else if (element == EL_PENGUIN)
6853     {
6854       int newx, newy;
6855
6856       MovDelay[x][y] = 1;
6857
6858       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6859       {
6860         boolean first_horiz = RND(2);
6861         int new_move_dir = MovDir[x][y];
6862
6863         MovDir[x][y] =
6864           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6865         Moving2Blocked(x, y, &newx, &newy);
6866
6867         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6868           return;
6869
6870         MovDir[x][y] =
6871           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6872         Moving2Blocked(x, y, &newx, &newy);
6873
6874         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6875           return;
6876
6877         MovDir[x][y] = old_move_dir;
6878         return;
6879       }
6880     }
6881     else if (element == EL_SATELLITE)
6882     {
6883       int newx, newy;
6884
6885       MovDelay[x][y] = 1;
6886
6887       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6888       {
6889         boolean first_horiz = RND(2);
6890         int new_move_dir = MovDir[x][y];
6891
6892         MovDir[x][y] =
6893           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6894         Moving2Blocked(x, y, &newx, &newy);
6895
6896         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6897           return;
6898
6899         MovDir[x][y] =
6900           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6901         Moving2Blocked(x, y, &newx, &newy);
6902
6903         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6904           return;
6905
6906         MovDir[x][y] = old_move_dir;
6907         return;
6908       }
6909     }
6910     else if (element == EL_EMC_ANDROID)
6911     {
6912       static int check_pos[16] =
6913       {
6914         -1,             /*  0 => (invalid)          */
6915         7,              /*  1 => MV_LEFT            */
6916         3,              /*  2 => MV_RIGHT           */
6917         -1,             /*  3 => (invalid)          */
6918         1,              /*  4 =>            MV_UP   */
6919         0,              /*  5 => MV_LEFT  | MV_UP   */
6920         2,              /*  6 => MV_RIGHT | MV_UP   */
6921         -1,             /*  7 => (invalid)          */
6922         5,              /*  8 =>            MV_DOWN */
6923         6,              /*  9 => MV_LEFT  | MV_DOWN */
6924         4,              /* 10 => MV_RIGHT | MV_DOWN */
6925         -1,             /* 11 => (invalid)          */
6926         -1,             /* 12 => (invalid)          */
6927         -1,             /* 13 => (invalid)          */
6928         -1,             /* 14 => (invalid)          */
6929         -1,             /* 15 => (invalid)          */
6930       };
6931       static struct
6932       {
6933         int dx, dy;
6934         int dir;
6935       } check_xy[8] =
6936       {
6937         { -1, -1,       MV_LEFT  | MV_UP   },
6938         {  0, -1,                  MV_UP   },
6939         { +1, -1,       MV_RIGHT | MV_UP   },
6940         { +1,  0,       MV_RIGHT           },
6941         { +1, +1,       MV_RIGHT | MV_DOWN },
6942         {  0, +1,                  MV_DOWN },
6943         { -1, +1,       MV_LEFT  | MV_DOWN },
6944         { -1,  0,       MV_LEFT            },
6945       };
6946       int start_pos, check_order;
6947       boolean can_clone = FALSE;
6948       int i;
6949
6950       /* check if there is any free field around current position */
6951       for (i = 0; i < 8; i++)
6952       {
6953         int newx = x + check_xy[i].dx;
6954         int newy = y + check_xy[i].dy;
6955
6956         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6957         {
6958           can_clone = TRUE;
6959
6960           break;
6961         }
6962       }
6963
6964       if (can_clone)            /* randomly find an element to clone */
6965       {
6966         can_clone = FALSE;
6967
6968         start_pos = check_pos[RND(8)];
6969         check_order = (RND(2) ? -1 : +1);
6970
6971         for (i = 0; i < 8; i++)
6972         {
6973           int pos_raw = start_pos + i * check_order;
6974           int pos = (pos_raw + 8) % 8;
6975           int newx = x + check_xy[pos].dx;
6976           int newy = y + check_xy[pos].dy;
6977
6978           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6979           {
6980             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6981             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6982
6983             Store[x][y] = Feld[newx][newy];
6984
6985             can_clone = TRUE;
6986
6987             break;
6988           }
6989         }
6990       }
6991
6992       if (can_clone)            /* randomly find a direction to move */
6993       {
6994         can_clone = FALSE;
6995
6996         start_pos = check_pos[RND(8)];
6997         check_order = (RND(2) ? -1 : +1);
6998
6999         for (i = 0; i < 8; i++)
7000         {
7001           int pos_raw = start_pos + i * check_order;
7002           int pos = (pos_raw + 8) % 8;
7003           int newx = x + check_xy[pos].dx;
7004           int newy = y + check_xy[pos].dy;
7005           int new_move_dir = check_xy[pos].dir;
7006
7007           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7008           {
7009             MovDir[x][y] = new_move_dir;
7010             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7011
7012             can_clone = TRUE;
7013
7014             break;
7015           }
7016         }
7017       }
7018
7019       if (can_clone)            /* cloning and moving successful */
7020         return;
7021
7022       /* cannot clone -- try to move towards player */
7023
7024       start_pos = check_pos[MovDir[x][y] & 0x0f];
7025       check_order = (RND(2) ? -1 : +1);
7026
7027       for (i = 0; i < 3; i++)
7028       {
7029         /* first check start_pos, then previous/next or (next/previous) pos */
7030         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7031         int pos = (pos_raw + 8) % 8;
7032         int newx = x + check_xy[pos].dx;
7033         int newy = y + check_xy[pos].dy;
7034         int new_move_dir = check_xy[pos].dir;
7035
7036         if (IS_PLAYER(newx, newy))
7037           break;
7038
7039         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7040         {
7041           MovDir[x][y] = new_move_dir;
7042           MovDelay[x][y] = level.android_move_time * 8 + 1;
7043
7044           break;
7045         }
7046       }
7047     }
7048   }
7049   else if (move_pattern == MV_TURNING_LEFT ||
7050            move_pattern == MV_TURNING_RIGHT ||
7051            move_pattern == MV_TURNING_LEFT_RIGHT ||
7052            move_pattern == MV_TURNING_RIGHT_LEFT ||
7053            move_pattern == MV_TURNING_RANDOM ||
7054            move_pattern == MV_ALL_DIRECTIONS)
7055   {
7056     boolean can_turn_left =
7057       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7058     boolean can_turn_right =
7059       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7060
7061     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7062       return;
7063
7064     if (move_pattern == MV_TURNING_LEFT)
7065       MovDir[x][y] = left_dir;
7066     else if (move_pattern == MV_TURNING_RIGHT)
7067       MovDir[x][y] = right_dir;
7068     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7069       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7070     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7071       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7072     else if (move_pattern == MV_TURNING_RANDOM)
7073       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7074                       can_turn_right && !can_turn_left ? right_dir :
7075                       RND(2) ? left_dir : right_dir);
7076     else if (can_turn_left && can_turn_right)
7077       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7078     else if (can_turn_left)
7079       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7080     else if (can_turn_right)
7081       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7082     else
7083       MovDir[x][y] = back_dir;
7084
7085     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7086   }
7087   else if (move_pattern == MV_HORIZONTAL ||
7088            move_pattern == MV_VERTICAL)
7089   {
7090     if (move_pattern & old_move_dir)
7091       MovDir[x][y] = back_dir;
7092     else if (move_pattern == MV_HORIZONTAL)
7093       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7094     else if (move_pattern == MV_VERTICAL)
7095       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7096
7097     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7098   }
7099   else if (move_pattern & MV_ANY_DIRECTION)
7100   {
7101     MovDir[x][y] = move_pattern;
7102     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7103   }
7104   else if (move_pattern & MV_WIND_DIRECTION)
7105   {
7106     MovDir[x][y] = game.wind_direction;
7107     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7108   }
7109   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7110   {
7111     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7112       MovDir[x][y] = left_dir;
7113     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7114       MovDir[x][y] = right_dir;
7115
7116     if (MovDir[x][y] != old_move_dir)
7117       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7118   }
7119   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7120   {
7121     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7122       MovDir[x][y] = right_dir;
7123     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7124       MovDir[x][y] = left_dir;
7125
7126     if (MovDir[x][y] != old_move_dir)
7127       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7128   }
7129   else if (move_pattern == MV_TOWARDS_PLAYER ||
7130            move_pattern == MV_AWAY_FROM_PLAYER)
7131   {
7132     int attr_x = -1, attr_y = -1;
7133     int newx, newy;
7134     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7135
7136     if (AllPlayersGone)
7137     {
7138       attr_x = ExitX;
7139       attr_y = ExitY;
7140     }
7141     else
7142     {
7143       int i;
7144
7145       for (i = 0; i < MAX_PLAYERS; i++)
7146       {
7147         struct PlayerInfo *player = &stored_player[i];
7148         int jx = player->jx, jy = player->jy;
7149
7150         if (!player->active)
7151           continue;
7152
7153         if (attr_x == -1 ||
7154             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7155         {
7156           attr_x = jx;
7157           attr_y = jy;
7158         }
7159       }
7160     }
7161
7162     MovDir[x][y] = MV_NONE;
7163     if (attr_x < x)
7164       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7165     else if (attr_x > x)
7166       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7167     if (attr_y < y)
7168       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7169     else if (attr_y > y)
7170       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7171
7172     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7173
7174     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7175     {
7176       boolean first_horiz = RND(2);
7177       int new_move_dir = MovDir[x][y];
7178
7179       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7180       {
7181         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7182         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7183
7184         return;
7185       }
7186
7187       MovDir[x][y] =
7188         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7189       Moving2Blocked(x, y, &newx, &newy);
7190
7191       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7192         return;
7193
7194       MovDir[x][y] =
7195         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196       Moving2Blocked(x, y, &newx, &newy);
7197
7198       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7199         return;
7200
7201       MovDir[x][y] = old_move_dir;
7202     }
7203   }
7204   else if (move_pattern == MV_WHEN_PUSHED ||
7205            move_pattern == MV_WHEN_DROPPED)
7206   {
7207     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7208       MovDir[x][y] = MV_NONE;
7209
7210     MovDelay[x][y] = 0;
7211   }
7212   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7213   {
7214     static int test_xy[7][2] =
7215     {
7216       { 0, -1 },
7217       { -1, 0 },
7218       { +1, 0 },
7219       { 0, +1 },
7220       { 0, -1 },
7221       { -1, 0 },
7222       { +1, 0 },
7223     };
7224     static int test_dir[7] =
7225     {
7226       MV_UP,
7227       MV_LEFT,
7228       MV_RIGHT,
7229       MV_DOWN,
7230       MV_UP,
7231       MV_LEFT,
7232       MV_RIGHT,
7233     };
7234     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7235     int move_preference = -1000000;     /* start with very low preference */
7236     int new_move_dir = MV_NONE;
7237     int start_test = RND(4);
7238     int i;
7239
7240     for (i = 0; i < NUM_DIRECTIONS; i++)
7241     {
7242       int move_dir = test_dir[start_test + i];
7243       int move_dir_preference;
7244
7245       xx = x + test_xy[start_test + i][0];
7246       yy = y + test_xy[start_test + i][1];
7247
7248       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7249           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7250       {
7251         new_move_dir = move_dir;
7252
7253         break;
7254       }
7255
7256       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7257         continue;
7258
7259       move_dir_preference = -1 * RunnerVisit[xx][yy];
7260       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7261         move_dir_preference = PlayerVisit[xx][yy];
7262
7263       if (move_dir_preference > move_preference)
7264       {
7265         /* prefer field that has not been visited for the longest time */
7266         move_preference = move_dir_preference;
7267         new_move_dir = move_dir;
7268       }
7269       else if (move_dir_preference == move_preference &&
7270                move_dir == old_move_dir)
7271       {
7272         /* prefer last direction when all directions are preferred equally */
7273         move_preference = move_dir_preference;
7274         new_move_dir = move_dir;
7275       }
7276     }
7277
7278     MovDir[x][y] = new_move_dir;
7279     if (old_move_dir != new_move_dir)
7280       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7281   }
7282 }
7283
7284 static void TurnRound(int x, int y)
7285 {
7286   int direction = MovDir[x][y];
7287
7288   TurnRoundExt(x, y);
7289
7290   GfxDir[x][y] = MovDir[x][y];
7291
7292   if (direction != MovDir[x][y])
7293     GfxFrame[x][y] = 0;
7294
7295   if (MovDelay[x][y])
7296     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7297
7298   ResetGfxFrame(x, y);
7299 }
7300
7301 static boolean JustBeingPushed(int x, int y)
7302 {
7303   int i;
7304
7305   for (i = 0; i < MAX_PLAYERS; i++)
7306   {
7307     struct PlayerInfo *player = &stored_player[i];
7308
7309     if (player->active && player->is_pushing && player->MovPos)
7310     {
7311       int next_jx = player->jx + (player->jx - player->last_jx);
7312       int next_jy = player->jy + (player->jy - player->last_jy);
7313
7314       if (x == next_jx && y == next_jy)
7315         return TRUE;
7316     }
7317   }
7318
7319   return FALSE;
7320 }
7321
7322 void StartMoving(int x, int y)
7323 {
7324   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7325   int element = Feld[x][y];
7326
7327   if (Stop[x][y])
7328     return;
7329
7330   if (MovDelay[x][y] == 0)
7331     GfxAction[x][y] = ACTION_DEFAULT;
7332
7333   if (CAN_FALL(element) && y < lev_fieldy - 1)
7334   {
7335     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7336         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7337       if (JustBeingPushed(x, y))
7338         return;
7339
7340     if (element == EL_QUICKSAND_FULL)
7341     {
7342       if (IS_FREE(x, y + 1))
7343       {
7344         InitMovingField(x, y, MV_DOWN);
7345         started_moving = TRUE;
7346
7347         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7348 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7349         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7350           Store[x][y] = EL_ROCK;
7351 #else
7352         Store[x][y] = EL_ROCK;
7353 #endif
7354
7355         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7356       }
7357       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7358       {
7359         if (!MovDelay[x][y])
7360         {
7361           MovDelay[x][y] = TILEY + 1;
7362
7363           ResetGfxAnimation(x, y);
7364           ResetGfxAnimation(x, y + 1);
7365         }
7366
7367         if (MovDelay[x][y])
7368         {
7369           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7370           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7371
7372           MovDelay[x][y]--;
7373           if (MovDelay[x][y])
7374             return;
7375         }
7376
7377         Feld[x][y] = EL_QUICKSAND_EMPTY;
7378         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7379         Store[x][y + 1] = Store[x][y];
7380         Store[x][y] = 0;
7381
7382         PlayLevelSoundAction(x, y, ACTION_FILLING);
7383       }
7384       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7385       {
7386         if (!MovDelay[x][y])
7387         {
7388           MovDelay[x][y] = TILEY + 1;
7389
7390           ResetGfxAnimation(x, y);
7391           ResetGfxAnimation(x, y + 1);
7392         }
7393
7394         if (MovDelay[x][y])
7395         {
7396           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7397           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7398
7399           MovDelay[x][y]--;
7400           if (MovDelay[x][y])
7401             return;
7402         }
7403
7404         Feld[x][y] = EL_QUICKSAND_EMPTY;
7405         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7406         Store[x][y + 1] = Store[x][y];
7407         Store[x][y] = 0;
7408
7409         PlayLevelSoundAction(x, y, ACTION_FILLING);
7410       }
7411     }
7412     else if (element == EL_QUICKSAND_FAST_FULL)
7413     {
7414       if (IS_FREE(x, y + 1))
7415       {
7416         InitMovingField(x, y, MV_DOWN);
7417         started_moving = TRUE;
7418
7419         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7420 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7421         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7422           Store[x][y] = EL_ROCK;
7423 #else
7424         Store[x][y] = EL_ROCK;
7425 #endif
7426
7427         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7428       }
7429       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7430       {
7431         if (!MovDelay[x][y])
7432         {
7433           MovDelay[x][y] = TILEY + 1;
7434
7435           ResetGfxAnimation(x, y);
7436           ResetGfxAnimation(x, y + 1);
7437         }
7438
7439         if (MovDelay[x][y])
7440         {
7441           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7442           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7443
7444           MovDelay[x][y]--;
7445           if (MovDelay[x][y])
7446             return;
7447         }
7448
7449         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7450         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7451         Store[x][y + 1] = Store[x][y];
7452         Store[x][y] = 0;
7453
7454         PlayLevelSoundAction(x, y, ACTION_FILLING);
7455       }
7456       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7457       {
7458         if (!MovDelay[x][y])
7459         {
7460           MovDelay[x][y] = TILEY + 1;
7461
7462           ResetGfxAnimation(x, y);
7463           ResetGfxAnimation(x, y + 1);
7464         }
7465
7466         if (MovDelay[x][y])
7467         {
7468           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7469           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7470
7471           MovDelay[x][y]--;
7472           if (MovDelay[x][y])
7473             return;
7474         }
7475
7476         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7477         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7478         Store[x][y + 1] = Store[x][y];
7479         Store[x][y] = 0;
7480
7481         PlayLevelSoundAction(x, y, ACTION_FILLING);
7482       }
7483     }
7484     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7485              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7486     {
7487       InitMovingField(x, y, MV_DOWN);
7488       started_moving = TRUE;
7489
7490       Feld[x][y] = EL_QUICKSAND_FILLING;
7491       Store[x][y] = element;
7492
7493       PlayLevelSoundAction(x, y, ACTION_FILLING);
7494     }
7495     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7496              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7497     {
7498       InitMovingField(x, y, MV_DOWN);
7499       started_moving = TRUE;
7500
7501       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7502       Store[x][y] = element;
7503
7504       PlayLevelSoundAction(x, y, ACTION_FILLING);
7505     }
7506     else if (element == EL_MAGIC_WALL_FULL)
7507     {
7508       if (IS_FREE(x, y + 1))
7509       {
7510         InitMovingField(x, y, MV_DOWN);
7511         started_moving = TRUE;
7512
7513         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7514         Store[x][y] = EL_CHANGED(Store[x][y]);
7515       }
7516       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7517       {
7518         if (!MovDelay[x][y])
7519           MovDelay[x][y] = TILEY / 4 + 1;
7520
7521         if (MovDelay[x][y])
7522         {
7523           MovDelay[x][y]--;
7524           if (MovDelay[x][y])
7525             return;
7526         }
7527
7528         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7529         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7530         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7531         Store[x][y] = 0;
7532       }
7533     }
7534     else if (element == EL_BD_MAGIC_WALL_FULL)
7535     {
7536       if (IS_FREE(x, y + 1))
7537       {
7538         InitMovingField(x, y, MV_DOWN);
7539         started_moving = TRUE;
7540
7541         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7542         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7543       }
7544       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7545       {
7546         if (!MovDelay[x][y])
7547           MovDelay[x][y] = TILEY / 4 + 1;
7548
7549         if (MovDelay[x][y])
7550         {
7551           MovDelay[x][y]--;
7552           if (MovDelay[x][y])
7553             return;
7554         }
7555
7556         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7557         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7558         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7559         Store[x][y] = 0;
7560       }
7561     }
7562     else if (element == EL_DC_MAGIC_WALL_FULL)
7563     {
7564       if (IS_FREE(x, y + 1))
7565       {
7566         InitMovingField(x, y, MV_DOWN);
7567         started_moving = TRUE;
7568
7569         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7570         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7571       }
7572       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7573       {
7574         if (!MovDelay[x][y])
7575           MovDelay[x][y] = TILEY / 4 + 1;
7576
7577         if (MovDelay[x][y])
7578         {
7579           MovDelay[x][y]--;
7580           if (MovDelay[x][y])
7581             return;
7582         }
7583
7584         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7585         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7586         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7587         Store[x][y] = 0;
7588       }
7589     }
7590     else if ((CAN_PASS_MAGIC_WALL(element) &&
7591               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7592                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7593              (CAN_PASS_DC_MAGIC_WALL(element) &&
7594               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7595
7596     {
7597       InitMovingField(x, y, MV_DOWN);
7598       started_moving = TRUE;
7599
7600       Feld[x][y] =
7601         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7602          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7603          EL_DC_MAGIC_WALL_FILLING);
7604       Store[x][y] = element;
7605     }
7606     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7607     {
7608       SplashAcid(x, y + 1);
7609
7610       InitMovingField(x, y, MV_DOWN);
7611       started_moving = TRUE;
7612
7613       Store[x][y] = EL_ACID;
7614     }
7615     else if (
7616              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7617               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7618              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7619               CAN_FALL(element) && WasJustFalling[x][y] &&
7620               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7621
7622              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7623               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7624               (Feld[x][y + 1] == EL_BLOCKED)))
7625     {
7626       /* this is needed for a special case not covered by calling "Impact()"
7627          from "ContinueMoving()": if an element moves to a tile directly below
7628          another element which was just falling on that tile (which was empty
7629          in the previous frame), the falling element above would just stop
7630          instead of smashing the element below (in previous version, the above
7631          element was just checked for "moving" instead of "falling", resulting
7632          in incorrect smashes caused by horizontal movement of the above
7633          element; also, the case of the player being the element to smash was
7634          simply not covered here... :-/ ) */
7635
7636       CheckCollision[x][y] = 0;
7637       CheckImpact[x][y] = 0;
7638
7639       Impact(x, y);
7640     }
7641     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7642     {
7643       if (MovDir[x][y] == MV_NONE)
7644       {
7645         InitMovingField(x, y, MV_DOWN);
7646         started_moving = TRUE;
7647       }
7648     }
7649     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7650     {
7651       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7652         MovDir[x][y] = MV_DOWN;
7653
7654       InitMovingField(x, y, MV_DOWN);
7655       started_moving = TRUE;
7656     }
7657     else if (element == EL_AMOEBA_DROP)
7658     {
7659       Feld[x][y] = EL_AMOEBA_GROWING;
7660       Store[x][y] = EL_AMOEBA_WET;
7661     }
7662     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7663               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7664              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7665              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7666     {
7667       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7668                                 (IS_FREE(x - 1, y + 1) ||
7669                                  Feld[x - 1][y + 1] == EL_ACID));
7670       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7671                                 (IS_FREE(x + 1, y + 1) ||
7672                                  Feld[x + 1][y + 1] == EL_ACID));
7673       boolean can_fall_any  = (can_fall_left || can_fall_right);
7674       boolean can_fall_both = (can_fall_left && can_fall_right);
7675       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7676
7677       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7678       {
7679         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7680           can_fall_right = FALSE;
7681         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7682           can_fall_left = FALSE;
7683         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7684           can_fall_right = FALSE;
7685         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7686           can_fall_left = FALSE;
7687
7688         can_fall_any  = (can_fall_left || can_fall_right);
7689         can_fall_both = FALSE;
7690       }
7691
7692       if (can_fall_both)
7693       {
7694         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7695           can_fall_right = FALSE;       /* slip down on left side */
7696         else
7697           can_fall_left = !(can_fall_right = RND(2));
7698
7699         can_fall_both = FALSE;
7700       }
7701
7702       if (can_fall_any)
7703       {
7704         /* if not determined otherwise, prefer left side for slipping down */
7705         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7706         started_moving = TRUE;
7707       }
7708     }
7709     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7710     {
7711       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7712       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7713       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7714       int belt_dir = game.belt_dir[belt_nr];
7715
7716       if ((belt_dir == MV_LEFT  && left_is_free) ||
7717           (belt_dir == MV_RIGHT && right_is_free))
7718       {
7719         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7720
7721         InitMovingField(x, y, belt_dir);
7722         started_moving = TRUE;
7723
7724         Pushed[x][y] = TRUE;
7725         Pushed[nextx][y] = TRUE;
7726
7727         GfxAction[x][y] = ACTION_DEFAULT;
7728       }
7729       else
7730       {
7731         MovDir[x][y] = 0;       /* if element was moving, stop it */
7732       }
7733     }
7734   }
7735
7736   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7737   if (CAN_MOVE(element) && !started_moving)
7738   {
7739     int move_pattern = element_info[element].move_pattern;
7740     int newx, newy;
7741
7742     Moving2Blocked(x, y, &newx, &newy);
7743
7744     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7745       return;
7746
7747     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7748         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7749     {
7750       WasJustMoving[x][y] = 0;
7751       CheckCollision[x][y] = 0;
7752
7753       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7754
7755       if (Feld[x][y] != element)        /* element has changed */
7756         return;
7757     }
7758
7759     if (!MovDelay[x][y])        /* start new movement phase */
7760     {
7761       /* all objects that can change their move direction after each step
7762          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7763
7764       if (element != EL_YAMYAM &&
7765           element != EL_DARK_YAMYAM &&
7766           element != EL_PACMAN &&
7767           !(move_pattern & MV_ANY_DIRECTION) &&
7768           move_pattern != MV_TURNING_LEFT &&
7769           move_pattern != MV_TURNING_RIGHT &&
7770           move_pattern != MV_TURNING_LEFT_RIGHT &&
7771           move_pattern != MV_TURNING_RIGHT_LEFT &&
7772           move_pattern != MV_TURNING_RANDOM)
7773       {
7774         TurnRound(x, y);
7775
7776         if (MovDelay[x][y] && (element == EL_BUG ||
7777                                element == EL_SPACESHIP ||
7778                                element == EL_SP_SNIKSNAK ||
7779                                element == EL_SP_ELECTRON ||
7780                                element == EL_MOLE))
7781           TEST_DrawLevelField(x, y);
7782       }
7783     }
7784
7785     if (MovDelay[x][y])         /* wait some time before next movement */
7786     {
7787       MovDelay[x][y]--;
7788
7789       if (element == EL_ROBOT ||
7790           element == EL_YAMYAM ||
7791           element == EL_DARK_YAMYAM)
7792       {
7793         DrawLevelElementAnimationIfNeeded(x, y, element);
7794         PlayLevelSoundAction(x, y, ACTION_WAITING);
7795       }
7796       else if (element == EL_SP_ELECTRON)
7797         DrawLevelElementAnimationIfNeeded(x, y, element);
7798       else if (element == EL_DRAGON)
7799       {
7800         int i;
7801         int dir = MovDir[x][y];
7802         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7803         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7804         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7805                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7806                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7807                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7808         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7809
7810         GfxAction[x][y] = ACTION_ATTACKING;
7811
7812         if (IS_PLAYER(x, y))
7813           DrawPlayerField(x, y);
7814         else
7815           TEST_DrawLevelField(x, y);
7816
7817         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7818
7819         for (i = 1; i <= 3; i++)
7820         {
7821           int xx = x + i * dx;
7822           int yy = y + i * dy;
7823           int sx = SCREENX(xx);
7824           int sy = SCREENY(yy);
7825           int flame_graphic = graphic + (i - 1);
7826
7827           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7828             break;
7829
7830           if (MovDelay[x][y])
7831           {
7832             int flamed = MovingOrBlocked2Element(xx, yy);
7833
7834             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7835               Bang(xx, yy);
7836             else
7837               RemoveMovingField(xx, yy);
7838
7839             ChangeDelay[xx][yy] = 0;
7840
7841             Feld[xx][yy] = EL_FLAMES;
7842
7843             if (IN_SCR_FIELD(sx, sy))
7844             {
7845               TEST_DrawLevelFieldCrumbled(xx, yy);
7846               DrawGraphic(sx, sy, flame_graphic, frame);
7847             }
7848           }
7849           else
7850           {
7851             if (Feld[xx][yy] == EL_FLAMES)
7852               Feld[xx][yy] = EL_EMPTY;
7853             TEST_DrawLevelField(xx, yy);
7854           }
7855         }
7856       }
7857
7858       if (MovDelay[x][y])       /* element still has to wait some time */
7859       {
7860         PlayLevelSoundAction(x, y, ACTION_WAITING);
7861
7862         return;
7863       }
7864     }
7865
7866     /* now make next step */
7867
7868     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7869
7870     if (DONT_COLLIDE_WITH(element) &&
7871         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7872         !PLAYER_ENEMY_PROTECTED(newx, newy))
7873     {
7874       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7875
7876       return;
7877     }
7878
7879     else if (CAN_MOVE_INTO_ACID(element) &&
7880              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7881              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7882              (MovDir[x][y] == MV_DOWN ||
7883               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7884     {
7885       SplashAcid(newx, newy);
7886       Store[x][y] = EL_ACID;
7887     }
7888     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7889     {
7890       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7891           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7892           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7893           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7894       {
7895         RemoveField(x, y);
7896         TEST_DrawLevelField(x, y);
7897
7898         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7899         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7900           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7901
7902         local_player->friends_still_needed--;
7903         if (!local_player->friends_still_needed &&
7904             !local_player->GameOver && AllPlayersGone)
7905           PlayerWins(local_player);
7906
7907         return;
7908       }
7909       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7910       {
7911         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7912           TEST_DrawLevelField(newx, newy);
7913         else
7914           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7915       }
7916       else if (!IS_FREE(newx, newy))
7917       {
7918         GfxAction[x][y] = ACTION_WAITING;
7919
7920         if (IS_PLAYER(x, y))
7921           DrawPlayerField(x, y);
7922         else
7923           TEST_DrawLevelField(x, y);
7924
7925         return;
7926       }
7927     }
7928     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7929     {
7930       if (IS_FOOD_PIG(Feld[newx][newy]))
7931       {
7932         if (IS_MOVING(newx, newy))
7933           RemoveMovingField(newx, newy);
7934         else
7935         {
7936           Feld[newx][newy] = EL_EMPTY;
7937           TEST_DrawLevelField(newx, newy);
7938         }
7939
7940         PlayLevelSound(x, y, SND_PIG_DIGGING);
7941       }
7942       else if (!IS_FREE(newx, newy))
7943       {
7944         if (IS_PLAYER(x, y))
7945           DrawPlayerField(x, y);
7946         else
7947           TEST_DrawLevelField(x, y);
7948
7949         return;
7950       }
7951     }
7952     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7953     {
7954       if (Store[x][y] != EL_EMPTY)
7955       {
7956         boolean can_clone = FALSE;
7957         int xx, yy;
7958
7959         /* check if element to clone is still there */
7960         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7961         {
7962           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7963           {
7964             can_clone = TRUE;
7965
7966             break;
7967           }
7968         }
7969
7970         /* cannot clone or target field not free anymore -- do not clone */
7971         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7972           Store[x][y] = EL_EMPTY;
7973       }
7974
7975       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7976       {
7977         if (IS_MV_DIAGONAL(MovDir[x][y]))
7978         {
7979           int diagonal_move_dir = MovDir[x][y];
7980           int stored = Store[x][y];
7981           int change_delay = 8;
7982           int graphic;
7983
7984           /* android is moving diagonally */
7985
7986           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7987
7988           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7989           GfxElement[x][y] = EL_EMC_ANDROID;
7990           GfxAction[x][y] = ACTION_SHRINKING;
7991           GfxDir[x][y] = diagonal_move_dir;
7992           ChangeDelay[x][y] = change_delay;
7993
7994           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7995                                    GfxDir[x][y]);
7996
7997           DrawLevelGraphicAnimation(x, y, graphic);
7998           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7999
8000           if (Feld[newx][newy] == EL_ACID)
8001           {
8002             SplashAcid(newx, newy);
8003
8004             return;
8005           }
8006
8007           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8008
8009           Store[newx][newy] = EL_EMC_ANDROID;
8010           GfxElement[newx][newy] = EL_EMC_ANDROID;
8011           GfxAction[newx][newy] = ACTION_GROWING;
8012           GfxDir[newx][newy] = diagonal_move_dir;
8013           ChangeDelay[newx][newy] = change_delay;
8014
8015           graphic = el_act_dir2img(GfxElement[newx][newy],
8016                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8017
8018           DrawLevelGraphicAnimation(newx, newy, graphic);
8019           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8020
8021           return;
8022         }
8023         else
8024         {
8025           Feld[newx][newy] = EL_EMPTY;
8026           TEST_DrawLevelField(newx, newy);
8027
8028           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8029         }
8030       }
8031       else if (!IS_FREE(newx, newy))
8032       {
8033         return;
8034       }
8035     }
8036     else if (IS_CUSTOM_ELEMENT(element) &&
8037              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8038     {
8039       if (!DigFieldByCE(newx, newy, element))
8040         return;
8041
8042       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8043       {
8044         RunnerVisit[x][y] = FrameCounter;
8045         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8046       }
8047     }
8048     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8049     {
8050       if (!IS_FREE(newx, newy))
8051       {
8052         if (IS_PLAYER(x, y))
8053           DrawPlayerField(x, y);
8054         else
8055           TEST_DrawLevelField(x, y);
8056
8057         return;
8058       }
8059       else
8060       {
8061         boolean wanna_flame = !RND(10);
8062         int dx = newx - x, dy = newy - y;
8063         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8064         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8065         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8066                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8067         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8068                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8069
8070         if ((wanna_flame ||
8071              IS_CLASSIC_ENEMY(element1) ||
8072              IS_CLASSIC_ENEMY(element2)) &&
8073             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8074             element1 != EL_FLAMES && element2 != EL_FLAMES)
8075         {
8076           ResetGfxAnimation(x, y);
8077           GfxAction[x][y] = ACTION_ATTACKING;
8078
8079           if (IS_PLAYER(x, y))
8080             DrawPlayerField(x, y);
8081           else
8082             TEST_DrawLevelField(x, y);
8083
8084           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8085
8086           MovDelay[x][y] = 50;
8087
8088           Feld[newx][newy] = EL_FLAMES;
8089           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8090             Feld[newx1][newy1] = EL_FLAMES;
8091           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8092             Feld[newx2][newy2] = EL_FLAMES;
8093
8094           return;
8095         }
8096       }
8097     }
8098     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8099              Feld[newx][newy] == EL_DIAMOND)
8100     {
8101       if (IS_MOVING(newx, newy))
8102         RemoveMovingField(newx, newy);
8103       else
8104       {
8105         Feld[newx][newy] = EL_EMPTY;
8106         TEST_DrawLevelField(newx, newy);
8107       }
8108
8109       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8110     }
8111     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8112              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8113     {
8114       if (AmoebaNr[newx][newy])
8115       {
8116         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8117         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8118             Feld[newx][newy] == EL_BD_AMOEBA)
8119           AmoebaCnt[AmoebaNr[newx][newy]]--;
8120       }
8121
8122       if (IS_MOVING(newx, newy))
8123       {
8124         RemoveMovingField(newx, newy);
8125       }
8126       else
8127       {
8128         Feld[newx][newy] = EL_EMPTY;
8129         TEST_DrawLevelField(newx, newy);
8130       }
8131
8132       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8133     }
8134     else if ((element == EL_PACMAN || element == EL_MOLE)
8135              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8136     {
8137       if (AmoebaNr[newx][newy])
8138       {
8139         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8140         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8141             Feld[newx][newy] == EL_BD_AMOEBA)
8142           AmoebaCnt[AmoebaNr[newx][newy]]--;
8143       }
8144
8145       if (element == EL_MOLE)
8146       {
8147         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8148         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8149
8150         ResetGfxAnimation(x, y);
8151         GfxAction[x][y] = ACTION_DIGGING;
8152         TEST_DrawLevelField(x, y);
8153
8154         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8155
8156         return;                         /* wait for shrinking amoeba */
8157       }
8158       else      /* element == EL_PACMAN */
8159       {
8160         Feld[newx][newy] = EL_EMPTY;
8161         TEST_DrawLevelField(newx, newy);
8162         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8163       }
8164     }
8165     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8166              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8167               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8168     {
8169       /* wait for shrinking amoeba to completely disappear */
8170       return;
8171     }
8172     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8173     {
8174       /* object was running against a wall */
8175
8176       TurnRound(x, y);
8177
8178       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8179         DrawLevelElementAnimation(x, y, element);
8180
8181       if (DONT_TOUCH(element))
8182         TestIfBadThingTouchesPlayer(x, y);
8183
8184       return;
8185     }
8186
8187     InitMovingField(x, y, MovDir[x][y]);
8188
8189     PlayLevelSoundAction(x, y, ACTION_MOVING);
8190   }
8191
8192   if (MovDir[x][y])
8193     ContinueMoving(x, y);
8194 }
8195
8196 void ContinueMoving(int x, int y)
8197 {
8198   int element = Feld[x][y];
8199   struct ElementInfo *ei = &element_info[element];
8200   int direction = MovDir[x][y];
8201   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8202   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8203   int newx = x + dx, newy = y + dy;
8204   int stored = Store[x][y];
8205   int stored_new = Store[newx][newy];
8206   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8207   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8208   boolean last_line = (newy == lev_fieldy - 1);
8209
8210   MovPos[x][y] += getElementMoveStepsize(x, y);
8211
8212   if (pushed_by_player) /* special case: moving object pushed by player */
8213     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8214
8215   if (ABS(MovPos[x][y]) < TILEX)
8216   {
8217     TEST_DrawLevelField(x, y);
8218
8219     return;     /* element is still moving */
8220   }
8221
8222   /* element reached destination field */
8223
8224   Feld[x][y] = EL_EMPTY;
8225   Feld[newx][newy] = element;
8226   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8227
8228   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8229   {
8230     element = Feld[newx][newy] = EL_ACID;
8231   }
8232   else if (element == EL_MOLE)
8233   {
8234     Feld[x][y] = EL_SAND;
8235
8236     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8237   }
8238   else if (element == EL_QUICKSAND_FILLING)
8239   {
8240     element = Feld[newx][newy] = get_next_element(element);
8241     Store[newx][newy] = Store[x][y];
8242   }
8243   else if (element == EL_QUICKSAND_EMPTYING)
8244   {
8245     Feld[x][y] = get_next_element(element);
8246     element = Feld[newx][newy] = Store[x][y];
8247   }
8248   else if (element == EL_QUICKSAND_FAST_FILLING)
8249   {
8250     element = Feld[newx][newy] = get_next_element(element);
8251     Store[newx][newy] = Store[x][y];
8252   }
8253   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8254   {
8255     Feld[x][y] = get_next_element(element);
8256     element = Feld[newx][newy] = Store[x][y];
8257   }
8258   else if (element == EL_MAGIC_WALL_FILLING)
8259   {
8260     element = Feld[newx][newy] = get_next_element(element);
8261     if (!game.magic_wall_active)
8262       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8263     Store[newx][newy] = Store[x][y];
8264   }
8265   else if (element == EL_MAGIC_WALL_EMPTYING)
8266   {
8267     Feld[x][y] = get_next_element(element);
8268     if (!game.magic_wall_active)
8269       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8270     element = Feld[newx][newy] = Store[x][y];
8271
8272     InitField(newx, newy, FALSE);
8273   }
8274   else if (element == EL_BD_MAGIC_WALL_FILLING)
8275   {
8276     element = Feld[newx][newy] = get_next_element(element);
8277     if (!game.magic_wall_active)
8278       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8279     Store[newx][newy] = Store[x][y];
8280   }
8281   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8282   {
8283     Feld[x][y] = get_next_element(element);
8284     if (!game.magic_wall_active)
8285       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8286     element = Feld[newx][newy] = Store[x][y];
8287
8288     InitField(newx, newy, FALSE);
8289   }
8290   else if (element == EL_DC_MAGIC_WALL_FILLING)
8291   {
8292     element = Feld[newx][newy] = get_next_element(element);
8293     if (!game.magic_wall_active)
8294       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8295     Store[newx][newy] = Store[x][y];
8296   }
8297   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8298   {
8299     Feld[x][y] = get_next_element(element);
8300     if (!game.magic_wall_active)
8301       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8302     element = Feld[newx][newy] = Store[x][y];
8303
8304     InitField(newx, newy, FALSE);
8305   }
8306   else if (element == EL_AMOEBA_DROPPING)
8307   {
8308     Feld[x][y] = get_next_element(element);
8309     element = Feld[newx][newy] = Store[x][y];
8310   }
8311   else if (element == EL_SOKOBAN_OBJECT)
8312   {
8313     if (Back[x][y])
8314       Feld[x][y] = Back[x][y];
8315
8316     if (Back[newx][newy])
8317       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8318
8319     Back[x][y] = Back[newx][newy] = 0;
8320   }
8321
8322   Store[x][y] = EL_EMPTY;
8323   MovPos[x][y] = 0;
8324   MovDir[x][y] = 0;
8325   MovDelay[x][y] = 0;
8326
8327   MovDelay[newx][newy] = 0;
8328
8329   if (CAN_CHANGE_OR_HAS_ACTION(element))
8330   {
8331     /* copy element change control values to new field */
8332     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8333     ChangePage[newx][newy]  = ChangePage[x][y];
8334     ChangeCount[newx][newy] = ChangeCount[x][y];
8335     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8336   }
8337
8338   CustomValue[newx][newy] = CustomValue[x][y];
8339
8340   ChangeDelay[x][y] = 0;
8341   ChangePage[x][y] = -1;
8342   ChangeCount[x][y] = 0;
8343   ChangeEvent[x][y] = -1;
8344
8345   CustomValue[x][y] = 0;
8346
8347   /* copy animation control values to new field */
8348   GfxFrame[newx][newy]  = GfxFrame[x][y];
8349   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8350   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8351   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8352
8353   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8354
8355   /* some elements can leave other elements behind after moving */
8356   if (ei->move_leave_element != EL_EMPTY &&
8357       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8358       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8359   {
8360     int move_leave_element = ei->move_leave_element;
8361
8362     /* this makes it possible to leave the removed element again */
8363     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8364       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8365
8366     Feld[x][y] = move_leave_element;
8367
8368     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8369       MovDir[x][y] = direction;
8370
8371     InitField(x, y, FALSE);
8372
8373     if (GFX_CRUMBLED(Feld[x][y]))
8374       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8375
8376     if (ELEM_IS_PLAYER(move_leave_element))
8377       RelocatePlayer(x, y, move_leave_element);
8378   }
8379
8380   /* do this after checking for left-behind element */
8381   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8382
8383   if (!CAN_MOVE(element) ||
8384       (CAN_FALL(element) && direction == MV_DOWN &&
8385        (element == EL_SPRING ||
8386         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8387         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8388     GfxDir[x][y] = MovDir[newx][newy] = 0;
8389
8390   TEST_DrawLevelField(x, y);
8391   TEST_DrawLevelField(newx, newy);
8392
8393   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8394
8395   /* prevent pushed element from moving on in pushed direction */
8396   if (pushed_by_player && CAN_MOVE(element) &&
8397       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8398       !(element_info[element].move_pattern & direction))
8399     TurnRound(newx, newy);
8400
8401   /* prevent elements on conveyor belt from moving on in last direction */
8402   if (pushed_by_conveyor && CAN_FALL(element) &&
8403       direction & MV_HORIZONTAL)
8404     MovDir[newx][newy] = 0;
8405
8406   if (!pushed_by_player)
8407   {
8408     int nextx = newx + dx, nexty = newy + dy;
8409     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8410
8411     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8412
8413     if (CAN_FALL(element) && direction == MV_DOWN)
8414       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8415
8416     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8417       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8418
8419     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8420       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8421   }
8422
8423   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8424   {
8425     TestIfBadThingTouchesPlayer(newx, newy);
8426     TestIfBadThingTouchesFriend(newx, newy);
8427
8428     if (!IS_CUSTOM_ELEMENT(element))
8429       TestIfBadThingTouchesOtherBadThing(newx, newy);
8430   }
8431   else if (element == EL_PENGUIN)
8432     TestIfFriendTouchesBadThing(newx, newy);
8433
8434   if (DONT_GET_HIT_BY(element))
8435   {
8436     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8437   }
8438
8439   /* give the player one last chance (one more frame) to move away */
8440   if (CAN_FALL(element) && direction == MV_DOWN &&
8441       (last_line || (!IS_FREE(x, newy + 1) &&
8442                      (!IS_PLAYER(x, newy + 1) ||
8443                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8444     Impact(x, newy);
8445
8446   if (pushed_by_player && !game.use_change_when_pushing_bug)
8447   {
8448     int push_side = MV_DIR_OPPOSITE(direction);
8449     struct PlayerInfo *player = PLAYERINFO(x, y);
8450
8451     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8452                                player->index_bit, push_side);
8453     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8454                                         player->index_bit, push_side);
8455   }
8456
8457   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8458     MovDelay[newx][newy] = 1;
8459
8460   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8461
8462   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8463   TestIfElementHitsCustomElement(newx, newy, direction);
8464   TestIfPlayerTouchesCustomElement(newx, newy);
8465   TestIfElementTouchesCustomElement(newx, newy);
8466
8467   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8468       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8469     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8470                              MV_DIR_OPPOSITE(direction));
8471 }
8472
8473 int AmoebeNachbarNr(int ax, int ay)
8474 {
8475   int i;
8476   int element = Feld[ax][ay];
8477   int group_nr = 0;
8478   static int xy[4][2] =
8479   {
8480     { 0, -1 },
8481     { -1, 0 },
8482     { +1, 0 },
8483     { 0, +1 }
8484   };
8485
8486   for (i = 0; i < NUM_DIRECTIONS; i++)
8487   {
8488     int x = ax + xy[i][0];
8489     int y = ay + xy[i][1];
8490
8491     if (!IN_LEV_FIELD(x, y))
8492       continue;
8493
8494     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8495       group_nr = AmoebaNr[x][y];
8496   }
8497
8498   return group_nr;
8499 }
8500
8501 void AmoebenVereinigen(int ax, int ay)
8502 {
8503   int i, x, y, xx, yy;
8504   int new_group_nr = AmoebaNr[ax][ay];
8505   static int xy[4][2] =
8506   {
8507     { 0, -1 },
8508     { -1, 0 },
8509     { +1, 0 },
8510     { 0, +1 }
8511   };
8512
8513   if (new_group_nr == 0)
8514     return;
8515
8516   for (i = 0; i < NUM_DIRECTIONS; i++)
8517   {
8518     x = ax + xy[i][0];
8519     y = ay + xy[i][1];
8520
8521     if (!IN_LEV_FIELD(x, y))
8522       continue;
8523
8524     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8525          Feld[x][y] == EL_BD_AMOEBA ||
8526          Feld[x][y] == EL_AMOEBA_DEAD) &&
8527         AmoebaNr[x][y] != new_group_nr)
8528     {
8529       int old_group_nr = AmoebaNr[x][y];
8530
8531       if (old_group_nr == 0)
8532         return;
8533
8534       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8535       AmoebaCnt[old_group_nr] = 0;
8536       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8537       AmoebaCnt2[old_group_nr] = 0;
8538
8539       SCAN_PLAYFIELD(xx, yy)
8540       {
8541         if (AmoebaNr[xx][yy] == old_group_nr)
8542           AmoebaNr[xx][yy] = new_group_nr;
8543       }
8544     }
8545   }
8546 }
8547
8548 void AmoebeUmwandeln(int ax, int ay)
8549 {
8550   int i, x, y;
8551
8552   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8553   {
8554     int group_nr = AmoebaNr[ax][ay];
8555
8556 #ifdef DEBUG
8557     if (group_nr == 0)
8558     {
8559       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8560       printf("AmoebeUmwandeln(): This should never happen!\n");
8561       return;
8562     }
8563 #endif
8564
8565     SCAN_PLAYFIELD(x, y)
8566     {
8567       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8568       {
8569         AmoebaNr[x][y] = 0;
8570         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8571       }
8572     }
8573
8574     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8575                             SND_AMOEBA_TURNING_TO_GEM :
8576                             SND_AMOEBA_TURNING_TO_ROCK));
8577     Bang(ax, ay);
8578   }
8579   else
8580   {
8581     static int xy[4][2] =
8582     {
8583       { 0, -1 },
8584       { -1, 0 },
8585       { +1, 0 },
8586       { 0, +1 }
8587     };
8588
8589     for (i = 0; i < NUM_DIRECTIONS; i++)
8590     {
8591       x = ax + xy[i][0];
8592       y = ay + xy[i][1];
8593
8594       if (!IN_LEV_FIELD(x, y))
8595         continue;
8596
8597       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8598       {
8599         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8600                               SND_AMOEBA_TURNING_TO_GEM :
8601                               SND_AMOEBA_TURNING_TO_ROCK));
8602         Bang(x, y);
8603       }
8604     }
8605   }
8606 }
8607
8608 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8609 {
8610   int x, y;
8611   int group_nr = AmoebaNr[ax][ay];
8612   boolean done = FALSE;
8613
8614 #ifdef DEBUG
8615   if (group_nr == 0)
8616   {
8617     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8618     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8619     return;
8620   }
8621 #endif
8622
8623   SCAN_PLAYFIELD(x, y)
8624   {
8625     if (AmoebaNr[x][y] == group_nr &&
8626         (Feld[x][y] == EL_AMOEBA_DEAD ||
8627          Feld[x][y] == EL_BD_AMOEBA ||
8628          Feld[x][y] == EL_AMOEBA_GROWING))
8629     {
8630       AmoebaNr[x][y] = 0;
8631       Feld[x][y] = new_element;
8632       InitField(x, y, FALSE);
8633       TEST_DrawLevelField(x, y);
8634       done = TRUE;
8635     }
8636   }
8637
8638   if (done)
8639     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8640                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8641                             SND_BD_AMOEBA_TURNING_TO_GEM));
8642 }
8643
8644 void AmoebeWaechst(int x, int y)
8645 {
8646   static unsigned int sound_delay = 0;
8647   static unsigned int sound_delay_value = 0;
8648
8649   if (!MovDelay[x][y])          /* start new growing cycle */
8650   {
8651     MovDelay[x][y] = 7;
8652
8653     if (DelayReached(&sound_delay, sound_delay_value))
8654     {
8655       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8656       sound_delay_value = 30;
8657     }
8658   }
8659
8660   if (MovDelay[x][y])           /* wait some time before growing bigger */
8661   {
8662     MovDelay[x][y]--;
8663     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8664     {
8665       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8666                                            6 - MovDelay[x][y]);
8667
8668       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8669     }
8670
8671     if (!MovDelay[x][y])
8672     {
8673       Feld[x][y] = Store[x][y];
8674       Store[x][y] = 0;
8675       TEST_DrawLevelField(x, y);
8676     }
8677   }
8678 }
8679
8680 void AmoebaDisappearing(int x, int y)
8681 {
8682   static unsigned int sound_delay = 0;
8683   static unsigned int sound_delay_value = 0;
8684
8685   if (!MovDelay[x][y])          /* start new shrinking cycle */
8686   {
8687     MovDelay[x][y] = 7;
8688
8689     if (DelayReached(&sound_delay, sound_delay_value))
8690       sound_delay_value = 30;
8691   }
8692
8693   if (MovDelay[x][y])           /* wait some time before shrinking */
8694   {
8695     MovDelay[x][y]--;
8696     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8697     {
8698       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8699                                            6 - MovDelay[x][y]);
8700
8701       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8702     }
8703
8704     if (!MovDelay[x][y])
8705     {
8706       Feld[x][y] = EL_EMPTY;
8707       TEST_DrawLevelField(x, y);
8708
8709       /* don't let mole enter this field in this cycle;
8710          (give priority to objects falling to this field from above) */
8711       Stop[x][y] = TRUE;
8712     }
8713   }
8714 }
8715
8716 void AmoebeAbleger(int ax, int ay)
8717 {
8718   int i;
8719   int element = Feld[ax][ay];
8720   int graphic = el2img(element);
8721   int newax = ax, neway = ay;
8722   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8723   static int xy[4][2] =
8724   {
8725     { 0, -1 },
8726     { -1, 0 },
8727     { +1, 0 },
8728     { 0, +1 }
8729   };
8730
8731   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8732   {
8733     Feld[ax][ay] = EL_AMOEBA_DEAD;
8734     TEST_DrawLevelField(ax, ay);
8735     return;
8736   }
8737
8738   if (IS_ANIMATED(graphic))
8739     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8740
8741   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8742     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8743
8744   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8745   {
8746     MovDelay[ax][ay]--;
8747     if (MovDelay[ax][ay])
8748       return;
8749   }
8750
8751   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8752   {
8753     int start = RND(4);
8754     int x = ax + xy[start][0];
8755     int y = ay + xy[start][1];
8756
8757     if (!IN_LEV_FIELD(x, y))
8758       return;
8759
8760     if (IS_FREE(x, y) ||
8761         CAN_GROW_INTO(Feld[x][y]) ||
8762         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8763         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8764     {
8765       newax = x;
8766       neway = y;
8767     }
8768
8769     if (newax == ax && neway == ay)
8770       return;
8771   }
8772   else                          /* normal or "filled" (BD style) amoeba */
8773   {
8774     int start = RND(4);
8775     boolean waiting_for_player = FALSE;
8776
8777     for (i = 0; i < NUM_DIRECTIONS; i++)
8778     {
8779       int j = (start + i) % 4;
8780       int x = ax + xy[j][0];
8781       int y = ay + xy[j][1];
8782
8783       if (!IN_LEV_FIELD(x, y))
8784         continue;
8785
8786       if (IS_FREE(x, y) ||
8787           CAN_GROW_INTO(Feld[x][y]) ||
8788           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8789           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8790       {
8791         newax = x;
8792         neway = y;
8793         break;
8794       }
8795       else if (IS_PLAYER(x, y))
8796         waiting_for_player = TRUE;
8797     }
8798
8799     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8800     {
8801       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8802       {
8803         Feld[ax][ay] = EL_AMOEBA_DEAD;
8804         TEST_DrawLevelField(ax, ay);
8805         AmoebaCnt[AmoebaNr[ax][ay]]--;
8806
8807         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8808         {
8809           if (element == EL_AMOEBA_FULL)
8810             AmoebeUmwandeln(ax, ay);
8811           else if (element == EL_BD_AMOEBA)
8812             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8813         }
8814       }
8815       return;
8816     }
8817     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8818     {
8819       /* amoeba gets larger by growing in some direction */
8820
8821       int new_group_nr = AmoebaNr[ax][ay];
8822
8823 #ifdef DEBUG
8824   if (new_group_nr == 0)
8825   {
8826     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8827     printf("AmoebeAbleger(): This should never happen!\n");
8828     return;
8829   }
8830 #endif
8831
8832       AmoebaNr[newax][neway] = new_group_nr;
8833       AmoebaCnt[new_group_nr]++;
8834       AmoebaCnt2[new_group_nr]++;
8835
8836       /* if amoeba touches other amoeba(s) after growing, unify them */
8837       AmoebenVereinigen(newax, neway);
8838
8839       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8840       {
8841         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8842         return;
8843       }
8844     }
8845   }
8846
8847   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8848       (neway == lev_fieldy - 1 && newax != ax))
8849   {
8850     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8851     Store[newax][neway] = element;
8852   }
8853   else if (neway == ay || element == EL_EMC_DRIPPER)
8854   {
8855     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8856
8857     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8858   }
8859   else
8860   {
8861     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8862     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8863     Store[ax][ay] = EL_AMOEBA_DROP;
8864     ContinueMoving(ax, ay);
8865     return;
8866   }
8867
8868   TEST_DrawLevelField(newax, neway);
8869 }
8870
8871 void Life(int ax, int ay)
8872 {
8873   int x1, y1, x2, y2;
8874   int life_time = 40;
8875   int element = Feld[ax][ay];
8876   int graphic = el2img(element);
8877   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8878                          level.biomaze);
8879   boolean changed = FALSE;
8880
8881   if (IS_ANIMATED(graphic))
8882     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8883
8884   if (Stop[ax][ay])
8885     return;
8886
8887   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8888     MovDelay[ax][ay] = life_time;
8889
8890   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8891   {
8892     MovDelay[ax][ay]--;
8893     if (MovDelay[ax][ay])
8894       return;
8895   }
8896
8897   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8898   {
8899     int xx = ax+x1, yy = ay+y1;
8900     int nachbarn = 0;
8901
8902     if (!IN_LEV_FIELD(xx, yy))
8903       continue;
8904
8905     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8906     {
8907       int x = xx+x2, y = yy+y2;
8908
8909       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8910         continue;
8911
8912       if (((Feld[x][y] == element ||
8913             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8914            !Stop[x][y]) ||
8915           (IS_FREE(x, y) && Stop[x][y]))
8916         nachbarn++;
8917     }
8918
8919     if (xx == ax && yy == ay)           /* field in the middle */
8920     {
8921       if (nachbarn < life_parameter[0] ||
8922           nachbarn > life_parameter[1])
8923       {
8924         Feld[xx][yy] = EL_EMPTY;
8925         if (!Stop[xx][yy])
8926           TEST_DrawLevelField(xx, yy);
8927         Stop[xx][yy] = TRUE;
8928         changed = TRUE;
8929       }
8930     }
8931     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8932     {                                   /* free border field */
8933       if (nachbarn >= life_parameter[2] &&
8934           nachbarn <= life_parameter[3])
8935       {
8936         Feld[xx][yy] = element;
8937         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8938         if (!Stop[xx][yy])
8939           TEST_DrawLevelField(xx, yy);
8940         Stop[xx][yy] = TRUE;
8941         changed = TRUE;
8942       }
8943     }
8944   }
8945
8946   if (changed)
8947     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8948                    SND_GAME_OF_LIFE_GROWING);
8949 }
8950
8951 static void InitRobotWheel(int x, int y)
8952 {
8953   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8954 }
8955
8956 static void RunRobotWheel(int x, int y)
8957 {
8958   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8959 }
8960
8961 static void StopRobotWheel(int x, int y)
8962 {
8963   if (ZX == x && ZY == y)
8964   {
8965     ZX = ZY = -1;
8966
8967     game.robot_wheel_active = FALSE;
8968   }
8969 }
8970
8971 static void InitTimegateWheel(int x, int y)
8972 {
8973   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8974 }
8975
8976 static void RunTimegateWheel(int x, int y)
8977 {
8978   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8979 }
8980
8981 static void InitMagicBallDelay(int x, int y)
8982 {
8983   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8984 }
8985
8986 static void ActivateMagicBall(int bx, int by)
8987 {
8988   int x, y;
8989
8990   if (level.ball_random)
8991   {
8992     int pos_border = RND(8);    /* select one of the eight border elements */
8993     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8994     int xx = pos_content % 3;
8995     int yy = pos_content / 3;
8996
8997     x = bx - 1 + xx;
8998     y = by - 1 + yy;
8999
9000     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9001       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9002   }
9003   else
9004   {
9005     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9006     {
9007       int xx = x - bx + 1;
9008       int yy = y - by + 1;
9009
9010       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9011         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9012     }
9013   }
9014
9015   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9016 }
9017
9018 void CheckExit(int x, int y)
9019 {
9020   if (local_player->gems_still_needed > 0 ||
9021       local_player->sokobanfields_still_needed > 0 ||
9022       local_player->lights_still_needed > 0)
9023   {
9024     int element = Feld[x][y];
9025     int graphic = el2img(element);
9026
9027     if (IS_ANIMATED(graphic))
9028       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9029
9030     return;
9031   }
9032
9033   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9034     return;
9035
9036   Feld[x][y] = EL_EXIT_OPENING;
9037
9038   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9039 }
9040
9041 void CheckExitEM(int x, int y)
9042 {
9043   if (local_player->gems_still_needed > 0 ||
9044       local_player->sokobanfields_still_needed > 0 ||
9045       local_player->lights_still_needed > 0)
9046   {
9047     int element = Feld[x][y];
9048     int graphic = el2img(element);
9049
9050     if (IS_ANIMATED(graphic))
9051       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9052
9053     return;
9054   }
9055
9056   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9057     return;
9058
9059   Feld[x][y] = EL_EM_EXIT_OPENING;
9060
9061   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9062 }
9063
9064 void CheckExitSteel(int x, int y)
9065 {
9066   if (local_player->gems_still_needed > 0 ||
9067       local_player->sokobanfields_still_needed > 0 ||
9068       local_player->lights_still_needed > 0)
9069   {
9070     int element = Feld[x][y];
9071     int graphic = el2img(element);
9072
9073     if (IS_ANIMATED(graphic))
9074       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9075
9076     return;
9077   }
9078
9079   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9080     return;
9081
9082   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9083
9084   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9085 }
9086
9087 void CheckExitSteelEM(int x, int y)
9088 {
9089   if (local_player->gems_still_needed > 0 ||
9090       local_player->sokobanfields_still_needed > 0 ||
9091       local_player->lights_still_needed > 0)
9092   {
9093     int element = Feld[x][y];
9094     int graphic = el2img(element);
9095
9096     if (IS_ANIMATED(graphic))
9097       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9098
9099     return;
9100   }
9101
9102   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9103     return;
9104
9105   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9106
9107   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9108 }
9109
9110 void CheckExitSP(int x, int y)
9111 {
9112   if (local_player->gems_still_needed > 0)
9113   {
9114     int element = Feld[x][y];
9115     int graphic = el2img(element);
9116
9117     if (IS_ANIMATED(graphic))
9118       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9119
9120     return;
9121   }
9122
9123   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9124     return;
9125
9126   Feld[x][y] = EL_SP_EXIT_OPENING;
9127
9128   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9129 }
9130
9131 static void CloseAllOpenTimegates()
9132 {
9133   int x, y;
9134
9135   SCAN_PLAYFIELD(x, y)
9136   {
9137     int element = Feld[x][y];
9138
9139     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9140     {
9141       Feld[x][y] = EL_TIMEGATE_CLOSING;
9142
9143       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9144     }
9145   }
9146 }
9147
9148 void DrawTwinkleOnField(int x, int y)
9149 {
9150   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9151     return;
9152
9153   if (Feld[x][y] == EL_BD_DIAMOND)
9154     return;
9155
9156   if (MovDelay[x][y] == 0)      /* next animation frame */
9157     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9158
9159   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9160   {
9161     MovDelay[x][y]--;
9162
9163     DrawLevelElementAnimation(x, y, Feld[x][y]);
9164
9165     if (MovDelay[x][y] != 0)
9166     {
9167       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9168                                            10 - MovDelay[x][y]);
9169
9170       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9171     }
9172   }
9173 }
9174
9175 void MauerWaechst(int x, int y)
9176 {
9177   int delay = 6;
9178
9179   if (!MovDelay[x][y])          /* next animation frame */
9180     MovDelay[x][y] = 3 * delay;
9181
9182   if (MovDelay[x][y])           /* wait some time before next frame */
9183   {
9184     MovDelay[x][y]--;
9185
9186     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9187     {
9188       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9189       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9190
9191       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9192     }
9193
9194     if (!MovDelay[x][y])
9195     {
9196       if (MovDir[x][y] == MV_LEFT)
9197       {
9198         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9199           TEST_DrawLevelField(x - 1, y);
9200       }
9201       else if (MovDir[x][y] == MV_RIGHT)
9202       {
9203         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9204           TEST_DrawLevelField(x + 1, y);
9205       }
9206       else if (MovDir[x][y] == MV_UP)
9207       {
9208         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9209           TEST_DrawLevelField(x, y - 1);
9210       }
9211       else
9212       {
9213         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9214           TEST_DrawLevelField(x, y + 1);
9215       }
9216
9217       Feld[x][y] = Store[x][y];
9218       Store[x][y] = 0;
9219       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9220       TEST_DrawLevelField(x, y);
9221     }
9222   }
9223 }
9224
9225 void MauerAbleger(int ax, int ay)
9226 {
9227   int element = Feld[ax][ay];
9228   int graphic = el2img(element);
9229   boolean oben_frei = FALSE, unten_frei = FALSE;
9230   boolean links_frei = FALSE, rechts_frei = FALSE;
9231   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9232   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9233   boolean new_wall = FALSE;
9234
9235   if (IS_ANIMATED(graphic))
9236     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9237
9238   if (!MovDelay[ax][ay])        /* start building new wall */
9239     MovDelay[ax][ay] = 6;
9240
9241   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9242   {
9243     MovDelay[ax][ay]--;
9244     if (MovDelay[ax][ay])
9245       return;
9246   }
9247
9248   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9249     oben_frei = TRUE;
9250   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9251     unten_frei = TRUE;
9252   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9253     links_frei = TRUE;
9254   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9255     rechts_frei = TRUE;
9256
9257   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9258       element == EL_EXPANDABLE_WALL_ANY)
9259   {
9260     if (oben_frei)
9261     {
9262       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9263       Store[ax][ay-1] = element;
9264       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9265       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9266         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9267                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9268       new_wall = TRUE;
9269     }
9270     if (unten_frei)
9271     {
9272       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9273       Store[ax][ay+1] = element;
9274       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9275       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9276         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9277                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9278       new_wall = TRUE;
9279     }
9280   }
9281
9282   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9283       element == EL_EXPANDABLE_WALL_ANY ||
9284       element == EL_EXPANDABLE_WALL ||
9285       element == EL_BD_EXPANDABLE_WALL)
9286   {
9287     if (links_frei)
9288     {
9289       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9290       Store[ax-1][ay] = element;
9291       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9292       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9293         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9294                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9295       new_wall = TRUE;
9296     }
9297
9298     if (rechts_frei)
9299     {
9300       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9301       Store[ax+1][ay] = element;
9302       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9303       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9304         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9305                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9306       new_wall = TRUE;
9307     }
9308   }
9309
9310   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9311     TEST_DrawLevelField(ax, ay);
9312
9313   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9314     oben_massiv = TRUE;
9315   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9316     unten_massiv = TRUE;
9317   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9318     links_massiv = TRUE;
9319   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9320     rechts_massiv = TRUE;
9321
9322   if (((oben_massiv && unten_massiv) ||
9323        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9324        element == EL_EXPANDABLE_WALL) &&
9325       ((links_massiv && rechts_massiv) ||
9326        element == EL_EXPANDABLE_WALL_VERTICAL))
9327     Feld[ax][ay] = EL_WALL;
9328
9329   if (new_wall)
9330     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9331 }
9332
9333 void MauerAblegerStahl(int ax, int ay)
9334 {
9335   int element = Feld[ax][ay];
9336   int graphic = el2img(element);
9337   boolean oben_frei = FALSE, unten_frei = FALSE;
9338   boolean links_frei = FALSE, rechts_frei = FALSE;
9339   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9340   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9341   boolean new_wall = FALSE;
9342
9343   if (IS_ANIMATED(graphic))
9344     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9345
9346   if (!MovDelay[ax][ay])        /* start building new wall */
9347     MovDelay[ax][ay] = 6;
9348
9349   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9350   {
9351     MovDelay[ax][ay]--;
9352     if (MovDelay[ax][ay])
9353       return;
9354   }
9355
9356   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9357     oben_frei = TRUE;
9358   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9359     unten_frei = TRUE;
9360   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9361     links_frei = TRUE;
9362   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9363     rechts_frei = TRUE;
9364
9365   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9366       element == EL_EXPANDABLE_STEELWALL_ANY)
9367   {
9368     if (oben_frei)
9369     {
9370       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9371       Store[ax][ay-1] = element;
9372       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9373       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9374         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9375                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9376       new_wall = TRUE;
9377     }
9378     if (unten_frei)
9379     {
9380       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9381       Store[ax][ay+1] = element;
9382       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9383       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9384         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9385                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9386       new_wall = TRUE;
9387     }
9388   }
9389
9390   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9391       element == EL_EXPANDABLE_STEELWALL_ANY)
9392   {
9393     if (links_frei)
9394     {
9395       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9396       Store[ax-1][ay] = element;
9397       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9398       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9399         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9400                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9401       new_wall = TRUE;
9402     }
9403
9404     if (rechts_frei)
9405     {
9406       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9407       Store[ax+1][ay] = element;
9408       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9409       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9410         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9411                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9412       new_wall = TRUE;
9413     }
9414   }
9415
9416   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9417     oben_massiv = TRUE;
9418   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9419     unten_massiv = TRUE;
9420   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9421     links_massiv = TRUE;
9422   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9423     rechts_massiv = TRUE;
9424
9425   if (((oben_massiv && unten_massiv) ||
9426        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9427       ((links_massiv && rechts_massiv) ||
9428        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9429     Feld[ax][ay] = EL_STEELWALL;
9430
9431   if (new_wall)
9432     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9433 }
9434
9435 void CheckForDragon(int x, int y)
9436 {
9437   int i, j;
9438   boolean dragon_found = FALSE;
9439   static int xy[4][2] =
9440   {
9441     { 0, -1 },
9442     { -1, 0 },
9443     { +1, 0 },
9444     { 0, +1 }
9445   };
9446
9447   for (i = 0; i < NUM_DIRECTIONS; i++)
9448   {
9449     for (j = 0; j < 4; j++)
9450     {
9451       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9452
9453       if (IN_LEV_FIELD(xx, yy) &&
9454           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9455       {
9456         if (Feld[xx][yy] == EL_DRAGON)
9457           dragon_found = TRUE;
9458       }
9459       else
9460         break;
9461     }
9462   }
9463
9464   if (!dragon_found)
9465   {
9466     for (i = 0; i < NUM_DIRECTIONS; i++)
9467     {
9468       for (j = 0; j < 3; j++)
9469       {
9470         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9471   
9472         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9473         {
9474           Feld[xx][yy] = EL_EMPTY;
9475           TEST_DrawLevelField(xx, yy);
9476         }
9477         else
9478           break;
9479       }
9480     }
9481   }
9482 }
9483
9484 static void InitBuggyBase(int x, int y)
9485 {
9486   int element = Feld[x][y];
9487   int activating_delay = FRAMES_PER_SECOND / 4;
9488
9489   ChangeDelay[x][y] =
9490     (element == EL_SP_BUGGY_BASE ?
9491      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9492      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9493      activating_delay :
9494      element == EL_SP_BUGGY_BASE_ACTIVE ?
9495      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9496 }
9497
9498 static void WarnBuggyBase(int x, int y)
9499 {
9500   int i;
9501   static int xy[4][2] =
9502   {
9503     { 0, -1 },
9504     { -1, 0 },
9505     { +1, 0 },
9506     { 0, +1 }
9507   };
9508
9509   for (i = 0; i < NUM_DIRECTIONS; i++)
9510   {
9511     int xx = x + xy[i][0];
9512     int yy = y + xy[i][1];
9513
9514     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9515     {
9516       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9517
9518       break;
9519     }
9520   }
9521 }
9522
9523 static void InitTrap(int x, int y)
9524 {
9525   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9526 }
9527
9528 static void ActivateTrap(int x, int y)
9529 {
9530   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9531 }
9532
9533 static void ChangeActiveTrap(int x, int y)
9534 {
9535   int graphic = IMG_TRAP_ACTIVE;
9536
9537   /* if new animation frame was drawn, correct crumbled sand border */
9538   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9539     TEST_DrawLevelFieldCrumbled(x, y);
9540 }
9541
9542 static int getSpecialActionElement(int element, int number, int base_element)
9543 {
9544   return (element != EL_EMPTY ? element :
9545           number != -1 ? base_element + number - 1 :
9546           EL_EMPTY);
9547 }
9548
9549 static int getModifiedActionNumber(int value_old, int operator, int operand,
9550                                    int value_min, int value_max)
9551 {
9552   int value_new = (operator == CA_MODE_SET      ? operand :
9553                    operator == CA_MODE_ADD      ? value_old + operand :
9554                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9555                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9556                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9557                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9558                    value_old);
9559
9560   return (value_new < value_min ? value_min :
9561           value_new > value_max ? value_max :
9562           value_new);
9563 }
9564
9565 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9566 {
9567   struct ElementInfo *ei = &element_info[element];
9568   struct ElementChangeInfo *change = &ei->change_page[page];
9569   int target_element = change->target_element;
9570   int action_type = change->action_type;
9571   int action_mode = change->action_mode;
9572   int action_arg = change->action_arg;
9573   int action_element = change->action_element;
9574   int i;
9575
9576   if (!change->has_action)
9577     return;
9578
9579   /* ---------- determine action paramater values -------------------------- */
9580
9581   int level_time_value =
9582     (level.time > 0 ? TimeLeft :
9583      TimePlayed);
9584
9585   int action_arg_element_raw =
9586     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9587      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9588      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9589      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9590      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9591      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9592      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9593      EL_EMPTY);
9594   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9595
9596   int action_arg_direction =
9597     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9598      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9599      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9600      change->actual_trigger_side :
9601      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9602      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9603      MV_NONE);
9604
9605   int action_arg_number_min =
9606     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9607      CA_ARG_MIN);
9608
9609   int action_arg_number_max =
9610     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9611      action_type == CA_SET_LEVEL_GEMS ? 999 :
9612      action_type == CA_SET_LEVEL_TIME ? 9999 :
9613      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9614      action_type == CA_SET_CE_VALUE ? 9999 :
9615      action_type == CA_SET_CE_SCORE ? 9999 :
9616      CA_ARG_MAX);
9617
9618   int action_arg_number_reset =
9619     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9620      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9621      action_type == CA_SET_LEVEL_TIME ? level.time :
9622      action_type == CA_SET_LEVEL_SCORE ? 0 :
9623      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9624      action_type == CA_SET_CE_SCORE ? 0 :
9625      0);
9626
9627   int action_arg_number =
9628     (action_arg <= CA_ARG_MAX ? action_arg :
9629      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9630      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9631      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9632      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9633      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9634      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9635      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9636      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9637      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9638      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9639      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9640      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9641      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9642      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9643      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9644      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9645      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9646      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9647      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9648      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9649      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9650      -1);
9651
9652   int action_arg_number_old =
9653     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9654      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9655      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9656      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9657      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9658      0);
9659
9660   int action_arg_number_new =
9661     getModifiedActionNumber(action_arg_number_old,
9662                             action_mode, action_arg_number,
9663                             action_arg_number_min, action_arg_number_max);
9664
9665   int trigger_player_bits =
9666     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9667      change->actual_trigger_player_bits : change->trigger_player);
9668
9669   int action_arg_player_bits =
9670     (action_arg >= CA_ARG_PLAYER_1 &&
9671      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9672      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9673      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9674      PLAYER_BITS_ANY);
9675
9676   /* ---------- execute action  -------------------------------------------- */
9677
9678   switch (action_type)
9679   {
9680     case CA_NO_ACTION:
9681     {
9682       return;
9683     }
9684
9685     /* ---------- level actions  ------------------------------------------- */
9686
9687     case CA_RESTART_LEVEL:
9688     {
9689       game.restart_level = TRUE;
9690
9691       break;
9692     }
9693
9694     case CA_SHOW_ENVELOPE:
9695     {
9696       int element = getSpecialActionElement(action_arg_element,
9697                                             action_arg_number, EL_ENVELOPE_1);
9698
9699       if (IS_ENVELOPE(element))
9700         local_player->show_envelope = element;
9701
9702       break;
9703     }
9704
9705     case CA_SET_LEVEL_TIME:
9706     {
9707       if (level.time > 0)       /* only modify limited time value */
9708       {
9709         TimeLeft = action_arg_number_new;
9710
9711         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9712
9713         DisplayGameControlValues();
9714
9715         if (!TimeLeft && setup.time_limit)
9716           for (i = 0; i < MAX_PLAYERS; i++)
9717             KillPlayer(&stored_player[i]);
9718       }
9719
9720       break;
9721     }
9722
9723     case CA_SET_LEVEL_SCORE:
9724     {
9725       local_player->score = action_arg_number_new;
9726
9727       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9728
9729       DisplayGameControlValues();
9730
9731       break;
9732     }
9733
9734     case CA_SET_LEVEL_GEMS:
9735     {
9736       local_player->gems_still_needed = action_arg_number_new;
9737
9738       game.snapshot.collected_item = TRUE;
9739
9740       game_panel_controls[GAME_PANEL_GEMS].value =
9741         local_player->gems_still_needed;
9742
9743       DisplayGameControlValues();
9744
9745       break;
9746     }
9747
9748     case CA_SET_LEVEL_WIND:
9749     {
9750       game.wind_direction = action_arg_direction;
9751
9752       break;
9753     }
9754
9755     case CA_SET_LEVEL_RANDOM_SEED:
9756     {
9757       /* ensure that setting a new random seed while playing is predictable */
9758       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9759
9760       break;
9761     }
9762
9763     /* ---------- player actions  ------------------------------------------ */
9764
9765     case CA_MOVE_PLAYER:
9766     {
9767       /* automatically move to the next field in specified direction */
9768       for (i = 0; i < MAX_PLAYERS; i++)
9769         if (trigger_player_bits & (1 << i))
9770           stored_player[i].programmed_action = action_arg_direction;
9771
9772       break;
9773     }
9774
9775     case CA_EXIT_PLAYER:
9776     {
9777       for (i = 0; i < MAX_PLAYERS; i++)
9778         if (action_arg_player_bits & (1 << i))
9779           ExitPlayer(&stored_player[i]);
9780
9781       if (AllPlayersGone)
9782         PlayerWins(local_player);
9783
9784       break;
9785     }
9786
9787     case CA_KILL_PLAYER:
9788     {
9789       for (i = 0; i < MAX_PLAYERS; i++)
9790         if (action_arg_player_bits & (1 << i))
9791           KillPlayer(&stored_player[i]);
9792
9793       break;
9794     }
9795
9796     case CA_SET_PLAYER_KEYS:
9797     {
9798       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9799       int element = getSpecialActionElement(action_arg_element,
9800                                             action_arg_number, EL_KEY_1);
9801
9802       if (IS_KEY(element))
9803       {
9804         for (i = 0; i < MAX_PLAYERS; i++)
9805         {
9806           if (trigger_player_bits & (1 << i))
9807           {
9808             stored_player[i].key[KEY_NR(element)] = key_state;
9809
9810             DrawGameDoorValues();
9811           }
9812         }
9813       }
9814
9815       break;
9816     }
9817
9818     case CA_SET_PLAYER_SPEED:
9819     {
9820       for (i = 0; i < MAX_PLAYERS; i++)
9821       {
9822         if (trigger_player_bits & (1 << i))
9823         {
9824           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9825
9826           if (action_arg == CA_ARG_SPEED_FASTER &&
9827               stored_player[i].cannot_move)
9828           {
9829             action_arg_number = STEPSIZE_VERY_SLOW;
9830           }
9831           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9832                    action_arg == CA_ARG_SPEED_FASTER)
9833           {
9834             action_arg_number = 2;
9835             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9836                            CA_MODE_MULTIPLY);
9837           }
9838           else if (action_arg == CA_ARG_NUMBER_RESET)
9839           {
9840             action_arg_number = level.initial_player_stepsize[i];
9841           }
9842
9843           move_stepsize =
9844             getModifiedActionNumber(move_stepsize,
9845                                     action_mode,
9846                                     action_arg_number,
9847                                     action_arg_number_min,
9848                                     action_arg_number_max);
9849
9850           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9851         }
9852       }
9853
9854       break;
9855     }
9856
9857     case CA_SET_PLAYER_SHIELD:
9858     {
9859       for (i = 0; i < MAX_PLAYERS; i++)
9860       {
9861         if (trigger_player_bits & (1 << i))
9862         {
9863           if (action_arg == CA_ARG_SHIELD_OFF)
9864           {
9865             stored_player[i].shield_normal_time_left = 0;
9866             stored_player[i].shield_deadly_time_left = 0;
9867           }
9868           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9869           {
9870             stored_player[i].shield_normal_time_left = 999999;
9871           }
9872           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9873           {
9874             stored_player[i].shield_normal_time_left = 999999;
9875             stored_player[i].shield_deadly_time_left = 999999;
9876           }
9877         }
9878       }
9879
9880       break;
9881     }
9882
9883     case CA_SET_PLAYER_GRAVITY:
9884     {
9885       for (i = 0; i < MAX_PLAYERS; i++)
9886       {
9887         if (trigger_player_bits & (1 << i))
9888         {
9889           stored_player[i].gravity =
9890             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9891              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9892              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9893              stored_player[i].gravity);
9894         }
9895       }
9896
9897       break;
9898     }
9899
9900     case CA_SET_PLAYER_ARTWORK:
9901     {
9902       for (i = 0; i < MAX_PLAYERS; i++)
9903       {
9904         if (trigger_player_bits & (1 << i))
9905         {
9906           int artwork_element = action_arg_element;
9907
9908           if (action_arg == CA_ARG_ELEMENT_RESET)
9909             artwork_element =
9910               (level.use_artwork_element[i] ? level.artwork_element[i] :
9911                stored_player[i].element_nr);
9912
9913           if (stored_player[i].artwork_element != artwork_element)
9914             stored_player[i].Frame = 0;
9915
9916           stored_player[i].artwork_element = artwork_element;
9917
9918           SetPlayerWaiting(&stored_player[i], FALSE);
9919
9920           /* set number of special actions for bored and sleeping animation */
9921           stored_player[i].num_special_action_bored =
9922             get_num_special_action(artwork_element,
9923                                    ACTION_BORING_1, ACTION_BORING_LAST);
9924           stored_player[i].num_special_action_sleeping =
9925             get_num_special_action(artwork_element,
9926                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9927         }
9928       }
9929
9930       break;
9931     }
9932
9933     case CA_SET_PLAYER_INVENTORY:
9934     {
9935       for (i = 0; i < MAX_PLAYERS; i++)
9936       {
9937         struct PlayerInfo *player = &stored_player[i];
9938         int j, k;
9939
9940         if (trigger_player_bits & (1 << i))
9941         {
9942           int inventory_element = action_arg_element;
9943
9944           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9945               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9946               action_arg == CA_ARG_ELEMENT_ACTION)
9947           {
9948             int element = inventory_element;
9949             int collect_count = element_info[element].collect_count_initial;
9950
9951             if (!IS_CUSTOM_ELEMENT(element))
9952               collect_count = 1;
9953
9954             if (collect_count == 0)
9955               player->inventory_infinite_element = element;
9956             else
9957               for (k = 0; k < collect_count; k++)
9958                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9959                   player->inventory_element[player->inventory_size++] =
9960                     element;
9961           }
9962           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9963                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9964                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9965           {
9966             if (player->inventory_infinite_element != EL_UNDEFINED &&
9967                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9968                                      action_arg_element_raw))
9969               player->inventory_infinite_element = EL_UNDEFINED;
9970
9971             for (k = 0, j = 0; j < player->inventory_size; j++)
9972             {
9973               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9974                                         action_arg_element_raw))
9975                 player->inventory_element[k++] = player->inventory_element[j];
9976             }
9977
9978             player->inventory_size = k;
9979           }
9980           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9981           {
9982             if (player->inventory_size > 0)
9983             {
9984               for (j = 0; j < player->inventory_size - 1; j++)
9985                 player->inventory_element[j] = player->inventory_element[j + 1];
9986
9987               player->inventory_size--;
9988             }
9989           }
9990           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9991           {
9992             if (player->inventory_size > 0)
9993               player->inventory_size--;
9994           }
9995           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9996           {
9997             player->inventory_infinite_element = EL_UNDEFINED;
9998             player->inventory_size = 0;
9999           }
10000           else if (action_arg == CA_ARG_INVENTORY_RESET)
10001           {
10002             player->inventory_infinite_element = EL_UNDEFINED;
10003             player->inventory_size = 0;
10004
10005             if (level.use_initial_inventory[i])
10006             {
10007               for (j = 0; j < level.initial_inventory_size[i]; j++)
10008               {
10009                 int element = level.initial_inventory_content[i][j];
10010                 int collect_count = element_info[element].collect_count_initial;
10011
10012                 if (!IS_CUSTOM_ELEMENT(element))
10013                   collect_count = 1;
10014
10015                 if (collect_count == 0)
10016                   player->inventory_infinite_element = element;
10017                 else
10018                   for (k = 0; k < collect_count; k++)
10019                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10020                       player->inventory_element[player->inventory_size++] =
10021                         element;
10022               }
10023             }
10024           }
10025         }
10026       }
10027
10028       break;
10029     }
10030
10031     /* ---------- CE actions  ---------------------------------------------- */
10032
10033     case CA_SET_CE_VALUE:
10034     {
10035       int last_ce_value = CustomValue[x][y];
10036
10037       CustomValue[x][y] = action_arg_number_new;
10038
10039       if (CustomValue[x][y] != last_ce_value)
10040       {
10041         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10042         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10043
10044         if (CustomValue[x][y] == 0)
10045         {
10046           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10047           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10048         }
10049       }
10050
10051       break;
10052     }
10053
10054     case CA_SET_CE_SCORE:
10055     {
10056       int last_ce_score = ei->collect_score;
10057
10058       ei->collect_score = action_arg_number_new;
10059
10060       if (ei->collect_score != last_ce_score)
10061       {
10062         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10063         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10064
10065         if (ei->collect_score == 0)
10066         {
10067           int xx, yy;
10068
10069           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10070           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10071
10072           /*
10073             This is a very special case that seems to be a mixture between
10074             CheckElementChange() and CheckTriggeredElementChange(): while
10075             the first one only affects single elements that are triggered
10076             directly, the second one affects multiple elements in the playfield
10077             that are triggered indirectly by another element. This is a third
10078             case: Changing the CE score always affects multiple identical CEs,
10079             so every affected CE must be checked, not only the single CE for
10080             which the CE score was changed in the first place (as every instance
10081             of that CE shares the same CE score, and therefore also can change)!
10082           */
10083           SCAN_PLAYFIELD(xx, yy)
10084           {
10085             if (Feld[xx][yy] == element)
10086               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10087                                  CE_SCORE_GETS_ZERO);
10088           }
10089         }
10090       }
10091
10092       break;
10093     }
10094
10095     case CA_SET_CE_ARTWORK:
10096     {
10097       int artwork_element = action_arg_element;
10098       boolean reset_frame = FALSE;
10099       int xx, yy;
10100
10101       if (action_arg == CA_ARG_ELEMENT_RESET)
10102         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10103                            element);
10104
10105       if (ei->gfx_element != artwork_element)
10106         reset_frame = TRUE;
10107
10108       ei->gfx_element = artwork_element;
10109
10110       SCAN_PLAYFIELD(xx, yy)
10111       {
10112         if (Feld[xx][yy] == element)
10113         {
10114           if (reset_frame)
10115           {
10116             ResetGfxAnimation(xx, yy);
10117             ResetRandomAnimationValue(xx, yy);
10118           }
10119
10120           TEST_DrawLevelField(xx, yy);
10121         }
10122       }
10123
10124       break;
10125     }
10126
10127     /* ---------- engine actions  ------------------------------------------ */
10128
10129     case CA_SET_ENGINE_SCAN_MODE:
10130     {
10131       InitPlayfieldScanMode(action_arg);
10132
10133       break;
10134     }
10135
10136     default:
10137       break;
10138   }
10139 }
10140
10141 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10142 {
10143   int old_element = Feld[x][y];
10144   int new_element = GetElementFromGroupElement(element);
10145   int previous_move_direction = MovDir[x][y];
10146   int last_ce_value = CustomValue[x][y];
10147   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10148   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10149   boolean add_player_onto_element = (new_element_is_player &&
10150                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10151                                      IS_WALKABLE(old_element));
10152
10153   if (!add_player_onto_element)
10154   {
10155     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10156       RemoveMovingField(x, y);
10157     else
10158       RemoveField(x, y);
10159
10160     Feld[x][y] = new_element;
10161
10162     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10163       MovDir[x][y] = previous_move_direction;
10164
10165     if (element_info[new_element].use_last_ce_value)
10166       CustomValue[x][y] = last_ce_value;
10167
10168     InitField_WithBug1(x, y, FALSE);
10169
10170     new_element = Feld[x][y];   /* element may have changed */
10171
10172     ResetGfxAnimation(x, y);
10173     ResetRandomAnimationValue(x, y);
10174
10175     TEST_DrawLevelField(x, y);
10176
10177     if (GFX_CRUMBLED(new_element))
10178       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10179   }
10180
10181   /* check if element under the player changes from accessible to unaccessible
10182      (needed for special case of dropping element which then changes) */
10183   /* (must be checked after creating new element for walkable group elements) */
10184   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10185       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10186   {
10187     Bang(x, y);
10188
10189     return;
10190   }
10191
10192   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10193   if (new_element_is_player)
10194     RelocatePlayer(x, y, new_element);
10195
10196   if (is_change)
10197     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10198
10199   TestIfBadThingTouchesPlayer(x, y);
10200   TestIfPlayerTouchesCustomElement(x, y);
10201   TestIfElementTouchesCustomElement(x, y);
10202 }
10203
10204 static void CreateField(int x, int y, int element)
10205 {
10206   CreateFieldExt(x, y, element, FALSE);
10207 }
10208
10209 static void CreateElementFromChange(int x, int y, int element)
10210 {
10211   element = GET_VALID_RUNTIME_ELEMENT(element);
10212
10213   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10214   {
10215     int old_element = Feld[x][y];
10216
10217     /* prevent changed element from moving in same engine frame
10218        unless both old and new element can either fall or move */
10219     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10220         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10221       Stop[x][y] = TRUE;
10222   }
10223
10224   CreateFieldExt(x, y, element, TRUE);
10225 }
10226
10227 static boolean ChangeElement(int x, int y, int element, int page)
10228 {
10229   struct ElementInfo *ei = &element_info[element];
10230   struct ElementChangeInfo *change = &ei->change_page[page];
10231   int ce_value = CustomValue[x][y];
10232   int ce_score = ei->collect_score;
10233   int target_element;
10234   int old_element = Feld[x][y];
10235
10236   /* always use default change event to prevent running into a loop */
10237   if (ChangeEvent[x][y] == -1)
10238     ChangeEvent[x][y] = CE_DELAY;
10239
10240   if (ChangeEvent[x][y] == CE_DELAY)
10241   {
10242     /* reset actual trigger element, trigger player and action element */
10243     change->actual_trigger_element = EL_EMPTY;
10244     change->actual_trigger_player = EL_EMPTY;
10245     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10246     change->actual_trigger_side = CH_SIDE_NONE;
10247     change->actual_trigger_ce_value = 0;
10248     change->actual_trigger_ce_score = 0;
10249   }
10250
10251   /* do not change elements more than a specified maximum number of changes */
10252   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10253     return FALSE;
10254
10255   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10256
10257   if (change->explode)
10258   {
10259     Bang(x, y);
10260
10261     return TRUE;
10262   }
10263
10264   if (change->use_target_content)
10265   {
10266     boolean complete_replace = TRUE;
10267     boolean can_replace[3][3];
10268     int xx, yy;
10269
10270     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10271     {
10272       boolean is_empty;
10273       boolean is_walkable;
10274       boolean is_diggable;
10275       boolean is_collectible;
10276       boolean is_removable;
10277       boolean is_destructible;
10278       int ex = x + xx - 1;
10279       int ey = y + yy - 1;
10280       int content_element = change->target_content.e[xx][yy];
10281       int e;
10282
10283       can_replace[xx][yy] = TRUE;
10284
10285       if (ex == x && ey == y)   /* do not check changing element itself */
10286         continue;
10287
10288       if (content_element == EL_EMPTY_SPACE)
10289       {
10290         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10291
10292         continue;
10293       }
10294
10295       if (!IN_LEV_FIELD(ex, ey))
10296       {
10297         can_replace[xx][yy] = FALSE;
10298         complete_replace = FALSE;
10299
10300         continue;
10301       }
10302
10303       e = Feld[ex][ey];
10304
10305       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10306         e = MovingOrBlocked2Element(ex, ey);
10307
10308       is_empty = (IS_FREE(ex, ey) ||
10309                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10310
10311       is_walkable     = (is_empty || IS_WALKABLE(e));
10312       is_diggable     = (is_empty || IS_DIGGABLE(e));
10313       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10314       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10315       is_removable    = (is_diggable || is_collectible);
10316
10317       can_replace[xx][yy] =
10318         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10319           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10320           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10321           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10322           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10323           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10324          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10325
10326       if (!can_replace[xx][yy])
10327         complete_replace = FALSE;
10328     }
10329
10330     if (!change->only_if_complete || complete_replace)
10331     {
10332       boolean something_has_changed = FALSE;
10333
10334       if (change->only_if_complete && change->use_random_replace &&
10335           RND(100) < change->random_percentage)
10336         return FALSE;
10337
10338       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10339       {
10340         int ex = x + xx - 1;
10341         int ey = y + yy - 1;
10342         int content_element;
10343
10344         if (can_replace[xx][yy] && (!change->use_random_replace ||
10345                                     RND(100) < change->random_percentage))
10346         {
10347           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10348             RemoveMovingField(ex, ey);
10349
10350           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10351
10352           content_element = change->target_content.e[xx][yy];
10353           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10354                                               ce_value, ce_score);
10355
10356           CreateElementFromChange(ex, ey, target_element);
10357
10358           something_has_changed = TRUE;
10359
10360           /* for symmetry reasons, freeze newly created border elements */
10361           if (ex != x || ey != y)
10362             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10363         }
10364       }
10365
10366       if (something_has_changed)
10367       {
10368         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10369         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10370       }
10371     }
10372   }
10373   else
10374   {
10375     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10376                                         ce_value, ce_score);
10377
10378     if (element == EL_DIAGONAL_GROWING ||
10379         element == EL_DIAGONAL_SHRINKING)
10380     {
10381       target_element = Store[x][y];
10382
10383       Store[x][y] = EL_EMPTY;
10384     }
10385
10386     CreateElementFromChange(x, y, target_element);
10387
10388     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10389     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10390   }
10391
10392   /* this uses direct change before indirect change */
10393   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10394
10395   return TRUE;
10396 }
10397
10398 static void HandleElementChange(int x, int y, int page)
10399 {
10400   int element = MovingOrBlocked2Element(x, y);
10401   struct ElementInfo *ei = &element_info[element];
10402   struct ElementChangeInfo *change = &ei->change_page[page];
10403   boolean handle_action_before_change = FALSE;
10404
10405 #ifdef DEBUG
10406   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10407       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10408   {
10409     printf("\n\n");
10410     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10411            x, y, element, element_info[element].token_name);
10412     printf("HandleElementChange(): This should never happen!\n");
10413     printf("\n\n");
10414   }
10415 #endif
10416
10417   /* this can happen with classic bombs on walkable, changing elements */
10418   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10419   {
10420     return;
10421   }
10422
10423   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10424   {
10425     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10426
10427     if (change->can_change)
10428     {
10429       /* !!! not clear why graphic animation should be reset at all here !!! */
10430       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10431       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10432
10433       /*
10434         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10435
10436         When using an animation frame delay of 1 (this only happens with
10437         "sp_zonk.moving.left/right" in the classic graphics), the default
10438         (non-moving) animation shows wrong animation frames (while the
10439         moving animation, like "sp_zonk.moving.left/right", is correct,
10440         so this graphical bug never shows up with the classic graphics).
10441         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10442         be drawn instead of the correct frames 0,1,2,3. This is caused by
10443         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10444         an element change: First when the change delay ("ChangeDelay[][]")
10445         counter has reached zero after decrementing, then a second time in
10446         the next frame (after "GfxFrame[][]" was already incremented) when
10447         "ChangeDelay[][]" is reset to the initial delay value again.
10448
10449         This causes frame 0 to be drawn twice, while the last frame won't
10450         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10451
10452         As some animations may already be cleverly designed around this bug
10453         (at least the "Snake Bite" snake tail animation does this), it cannot
10454         simply be fixed here without breaking such existing animations.
10455         Unfortunately, it cannot easily be detected if a graphics set was
10456         designed "before" or "after" the bug was fixed. As a workaround,
10457         a new graphics set option "game.graphics_engine_version" was added
10458         to be able to specify the game's major release version for which the
10459         graphics set was designed, which can then be used to decide if the
10460         bugfix should be used (version 4 and above) or not (version 3 or
10461         below, or if no version was specified at all, as with old sets).
10462
10463         (The wrong/fixed animation frames can be tested with the test level set
10464         "test_gfxframe" and level "000", which contains a specially prepared
10465         custom element at level position (x/y) == (11/9) which uses the zonk
10466         animation mentioned above. Using "game.graphics_engine_version: 4"
10467         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10468         This can also be seen from the debug output for this test element.)
10469       */
10470
10471       /* when a custom element is about to change (for example by change delay),
10472          do not reset graphic animation when the custom element is moving */
10473       if (game.graphics_engine_version < 4 &&
10474           !IS_MOVING(x, y))
10475       {
10476         ResetGfxAnimation(x, y);
10477         ResetRandomAnimationValue(x, y);
10478       }
10479
10480       if (change->pre_change_function)
10481         change->pre_change_function(x, y);
10482     }
10483   }
10484
10485   ChangeDelay[x][y]--;
10486
10487   if (ChangeDelay[x][y] != 0)           /* continue element change */
10488   {
10489     if (change->can_change)
10490     {
10491       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10492
10493       if (IS_ANIMATED(graphic))
10494         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10495
10496       if (change->change_function)
10497         change->change_function(x, y);
10498     }
10499   }
10500   else                                  /* finish element change */
10501   {
10502     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10503     {
10504       page = ChangePage[x][y];
10505       ChangePage[x][y] = -1;
10506
10507       change = &ei->change_page[page];
10508     }
10509
10510     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10511     {
10512       ChangeDelay[x][y] = 1;            /* try change after next move step */
10513       ChangePage[x][y] = page;          /* remember page to use for change */
10514
10515       return;
10516     }
10517
10518     /* special case: set new level random seed before changing element */
10519     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10520       handle_action_before_change = TRUE;
10521
10522     if (change->has_action && handle_action_before_change)
10523       ExecuteCustomElementAction(x, y, element, page);
10524
10525     if (change->can_change)
10526     {
10527       if (ChangeElement(x, y, element, page))
10528       {
10529         if (change->post_change_function)
10530           change->post_change_function(x, y);
10531       }
10532     }
10533
10534     if (change->has_action && !handle_action_before_change)
10535       ExecuteCustomElementAction(x, y, element, page);
10536   }
10537 }
10538
10539 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10540                                               int trigger_element,
10541                                               int trigger_event,
10542                                               int trigger_player,
10543                                               int trigger_side,
10544                                               int trigger_page)
10545 {
10546   boolean change_done_any = FALSE;
10547   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10548   int i;
10549
10550   if (!(trigger_events[trigger_element][trigger_event]))
10551     return FALSE;
10552
10553   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10554
10555   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10556   {
10557     int element = EL_CUSTOM_START + i;
10558     boolean change_done = FALSE;
10559     int p;
10560
10561     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10562         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10563       continue;
10564
10565     for (p = 0; p < element_info[element].num_change_pages; p++)
10566     {
10567       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10568
10569       if (change->can_change_or_has_action &&
10570           change->has_event[trigger_event] &&
10571           change->trigger_side & trigger_side &&
10572           change->trigger_player & trigger_player &&
10573           change->trigger_page & trigger_page_bits &&
10574           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10575       {
10576         change->actual_trigger_element = trigger_element;
10577         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10578         change->actual_trigger_player_bits = trigger_player;
10579         change->actual_trigger_side = trigger_side;
10580         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10581         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10582
10583         if ((change->can_change && !change_done) || change->has_action)
10584         {
10585           int x, y;
10586
10587           SCAN_PLAYFIELD(x, y)
10588           {
10589             if (Feld[x][y] == element)
10590             {
10591               if (change->can_change && !change_done)
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                 ChangeDelay[x][y] = 1;
10602                 ChangeEvent[x][y] = trigger_event;
10603
10604                 HandleElementChange(x, y, p);
10605               }
10606               else if (change->has_action)
10607               {
10608                 /* if element already changed in this frame, not only prevent
10609                    another element change (checked in ChangeElement()), but
10610                    also prevent additional element actions for this element */
10611
10612                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10613                     !level.use_action_after_change_bug)
10614                   continue;
10615
10616                 ExecuteCustomElementAction(x, y, element, p);
10617                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10618               }
10619             }
10620           }
10621
10622           if (change->can_change)
10623           {
10624             change_done = TRUE;
10625             change_done_any = TRUE;
10626           }
10627         }
10628       }
10629     }
10630   }
10631
10632   RECURSION_LOOP_DETECTION_END();
10633
10634   return change_done_any;
10635 }
10636
10637 static boolean CheckElementChangeExt(int x, int y,
10638                                      int element,
10639                                      int trigger_element,
10640                                      int trigger_event,
10641                                      int trigger_player,
10642                                      int trigger_side)
10643 {
10644   boolean change_done = FALSE;
10645   int p;
10646
10647   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10648       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10649     return FALSE;
10650
10651   if (Feld[x][y] == EL_BLOCKED)
10652   {
10653     Blocked2Moving(x, y, &x, &y);
10654     element = Feld[x][y];
10655   }
10656
10657   /* check if element has already changed or is about to change after moving */
10658   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10659        Feld[x][y] != element) ||
10660
10661       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10662        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10663         ChangePage[x][y] != -1)))
10664     return FALSE;
10665
10666   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10667
10668   for (p = 0; p < element_info[element].num_change_pages; p++)
10669   {
10670     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10671
10672     /* check trigger element for all events where the element that is checked
10673        for changing interacts with a directly adjacent element -- this is
10674        different to element changes that affect other elements to change on the
10675        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10676     boolean check_trigger_element =
10677       (trigger_event == CE_TOUCHING_X ||
10678        trigger_event == CE_HITTING_X ||
10679        trigger_event == CE_HIT_BY_X ||
10680        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10681
10682     if (change->can_change_or_has_action &&
10683         change->has_event[trigger_event] &&
10684         change->trigger_side & trigger_side &&
10685         change->trigger_player & trigger_player &&
10686         (!check_trigger_element ||
10687          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10688     {
10689       change->actual_trigger_element = trigger_element;
10690       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10691       change->actual_trigger_player_bits = trigger_player;
10692       change->actual_trigger_side = trigger_side;
10693       change->actual_trigger_ce_value = CustomValue[x][y];
10694       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10695
10696       /* special case: trigger element not at (x,y) position for some events */
10697       if (check_trigger_element)
10698       {
10699         static struct
10700         {
10701           int dx, dy;
10702         } move_xy[] =
10703           {
10704             {  0,  0 },
10705             { -1,  0 },
10706             { +1,  0 },
10707             {  0,  0 },
10708             {  0, -1 },
10709             {  0,  0 }, { 0, 0 }, { 0, 0 },
10710             {  0, +1 }
10711           };
10712
10713         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10714         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10715
10716         change->actual_trigger_ce_value = CustomValue[xx][yy];
10717         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10718       }
10719
10720       if (change->can_change && !change_done)
10721       {
10722         ChangeDelay[x][y] = 1;
10723         ChangeEvent[x][y] = trigger_event;
10724
10725         HandleElementChange(x, y, p);
10726
10727         change_done = TRUE;
10728       }
10729       else if (change->has_action)
10730       {
10731         ExecuteCustomElementAction(x, y, element, p);
10732         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10733       }
10734     }
10735   }
10736
10737   RECURSION_LOOP_DETECTION_END();
10738
10739   return change_done;
10740 }
10741
10742 static void PlayPlayerSound(struct PlayerInfo *player)
10743 {
10744   int jx = player->jx, jy = player->jy;
10745   int sound_element = player->artwork_element;
10746   int last_action = player->last_action_waiting;
10747   int action = player->action_waiting;
10748
10749   if (player->is_waiting)
10750   {
10751     if (action != last_action)
10752       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10753     else
10754       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10755   }
10756   else
10757   {
10758     if (action != last_action)
10759       StopSound(element_info[sound_element].sound[last_action]);
10760
10761     if (last_action == ACTION_SLEEPING)
10762       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10763   }
10764 }
10765
10766 static void PlayAllPlayersSound()
10767 {
10768   int i;
10769
10770   for (i = 0; i < MAX_PLAYERS; i++)
10771     if (stored_player[i].active)
10772       PlayPlayerSound(&stored_player[i]);
10773 }
10774
10775 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10776 {
10777   boolean last_waiting = player->is_waiting;
10778   int move_dir = player->MovDir;
10779
10780   player->dir_waiting = move_dir;
10781   player->last_action_waiting = player->action_waiting;
10782
10783   if (is_waiting)
10784   {
10785     if (!last_waiting)          /* not waiting -> waiting */
10786     {
10787       player->is_waiting = TRUE;
10788
10789       player->frame_counter_bored =
10790         FrameCounter +
10791         game.player_boring_delay_fixed +
10792         GetSimpleRandom(game.player_boring_delay_random);
10793       player->frame_counter_sleeping =
10794         FrameCounter +
10795         game.player_sleeping_delay_fixed +
10796         GetSimpleRandom(game.player_sleeping_delay_random);
10797
10798       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10799     }
10800
10801     if (game.player_sleeping_delay_fixed +
10802         game.player_sleeping_delay_random > 0 &&
10803         player->anim_delay_counter == 0 &&
10804         player->post_delay_counter == 0 &&
10805         FrameCounter >= player->frame_counter_sleeping)
10806       player->is_sleeping = TRUE;
10807     else if (game.player_boring_delay_fixed +
10808              game.player_boring_delay_random > 0 &&
10809              FrameCounter >= player->frame_counter_bored)
10810       player->is_bored = TRUE;
10811
10812     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10813                               player->is_bored ? ACTION_BORING :
10814                               ACTION_WAITING);
10815
10816     if (player->is_sleeping && player->use_murphy)
10817     {
10818       /* special case for sleeping Murphy when leaning against non-free tile */
10819
10820       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10821           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10822            !IS_MOVING(player->jx - 1, player->jy)))
10823         move_dir = MV_LEFT;
10824       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10825                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10826                 !IS_MOVING(player->jx + 1, player->jy)))
10827         move_dir = MV_RIGHT;
10828       else
10829         player->is_sleeping = FALSE;
10830
10831       player->dir_waiting = move_dir;
10832     }
10833
10834     if (player->is_sleeping)
10835     {
10836       if (player->num_special_action_sleeping > 0)
10837       {
10838         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10839         {
10840           int last_special_action = player->special_action_sleeping;
10841           int num_special_action = player->num_special_action_sleeping;
10842           int special_action =
10843             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10844              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10845              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10846              last_special_action + 1 : ACTION_SLEEPING);
10847           int special_graphic =
10848             el_act_dir2img(player->artwork_element, special_action, move_dir);
10849
10850           player->anim_delay_counter =
10851             graphic_info[special_graphic].anim_delay_fixed +
10852             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10853           player->post_delay_counter =
10854             graphic_info[special_graphic].post_delay_fixed +
10855             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10856
10857           player->special_action_sleeping = special_action;
10858         }
10859
10860         if (player->anim_delay_counter > 0)
10861         {
10862           player->action_waiting = player->special_action_sleeping;
10863           player->anim_delay_counter--;
10864         }
10865         else if (player->post_delay_counter > 0)
10866         {
10867           player->post_delay_counter--;
10868         }
10869       }
10870     }
10871     else if (player->is_bored)
10872     {
10873       if (player->num_special_action_bored > 0)
10874       {
10875         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10876         {
10877           int special_action =
10878             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10879           int special_graphic =
10880             el_act_dir2img(player->artwork_element, special_action, move_dir);
10881
10882           player->anim_delay_counter =
10883             graphic_info[special_graphic].anim_delay_fixed +
10884             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10885           player->post_delay_counter =
10886             graphic_info[special_graphic].post_delay_fixed +
10887             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10888
10889           player->special_action_bored = special_action;
10890         }
10891
10892         if (player->anim_delay_counter > 0)
10893         {
10894           player->action_waiting = player->special_action_bored;
10895           player->anim_delay_counter--;
10896         }
10897         else if (player->post_delay_counter > 0)
10898         {
10899           player->post_delay_counter--;
10900         }
10901       }
10902     }
10903   }
10904   else if (last_waiting)        /* waiting -> not waiting */
10905   {
10906     player->is_waiting = FALSE;
10907     player->is_bored = FALSE;
10908     player->is_sleeping = FALSE;
10909
10910     player->frame_counter_bored = -1;
10911     player->frame_counter_sleeping = -1;
10912
10913     player->anim_delay_counter = 0;
10914     player->post_delay_counter = 0;
10915
10916     player->dir_waiting = player->MovDir;
10917     player->action_waiting = ACTION_DEFAULT;
10918
10919     player->special_action_bored = ACTION_DEFAULT;
10920     player->special_action_sleeping = ACTION_DEFAULT;
10921   }
10922 }
10923
10924 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10925 {
10926   if ((!player->is_moving  && player->was_moving) ||
10927       (player->MovPos == 0 && player->was_moving) ||
10928       (player->is_snapping && !player->was_snapping) ||
10929       (player->is_dropping && !player->was_dropping))
10930   {
10931     if (!CheckSaveEngineSnapshotToList())
10932       return;
10933
10934     player->was_moving = FALSE;
10935     player->was_snapping = TRUE;
10936     player->was_dropping = TRUE;
10937   }
10938   else
10939   {
10940     if (player->is_moving)
10941       player->was_moving = TRUE;
10942
10943     if (!player->is_snapping)
10944       player->was_snapping = FALSE;
10945
10946     if (!player->is_dropping)
10947       player->was_dropping = FALSE;
10948   }
10949 }
10950
10951 static void CheckSingleStepMode(struct PlayerInfo *player)
10952 {
10953   if (tape.single_step && tape.recording && !tape.pausing)
10954   {
10955     /* as it is called "single step mode", just return to pause mode when the
10956        player stopped moving after one tile (or never starts moving at all) */
10957     if (!player->is_moving &&
10958         !player->is_pushing &&
10959         !player->is_dropping_pressed)
10960     {
10961       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10962       SnapField(player, 0, 0);                  /* stop snapping */
10963     }
10964   }
10965
10966   CheckSaveEngineSnapshot(player);
10967 }
10968
10969 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10970 {
10971   int left      = player_action & JOY_LEFT;
10972   int right     = player_action & JOY_RIGHT;
10973   int up        = player_action & JOY_UP;
10974   int down      = player_action & JOY_DOWN;
10975   int button1   = player_action & JOY_BUTTON_1;
10976   int button2   = player_action & JOY_BUTTON_2;
10977   int dx        = (left ? -1 : right ? 1 : 0);
10978   int dy        = (up   ? -1 : down  ? 1 : 0);
10979
10980   if (!player->active || tape.pausing)
10981     return 0;
10982
10983   if (player_action)
10984   {
10985     if (button1)
10986       SnapField(player, dx, dy);
10987     else
10988     {
10989       if (button2)
10990         DropElement(player);
10991
10992       MovePlayer(player, dx, dy);
10993     }
10994
10995     CheckSingleStepMode(player);
10996
10997     SetPlayerWaiting(player, FALSE);
10998
10999     return player_action;
11000   }
11001   else
11002   {
11003     /* no actions for this player (no input at player's configured device) */
11004
11005     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11006     SnapField(player, 0, 0);
11007     CheckGravityMovementWhenNotMoving(player);
11008
11009     if (player->MovPos == 0)
11010       SetPlayerWaiting(player, TRUE);
11011
11012     if (player->MovPos == 0)    /* needed for tape.playing */
11013       player->is_moving = FALSE;
11014
11015     player->is_dropping = FALSE;
11016     player->is_dropping_pressed = FALSE;
11017     player->drop_pressed_delay = 0;
11018
11019     CheckSingleStepMode(player);
11020
11021     return 0;
11022   }
11023 }
11024
11025 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11026                                          byte *tape_action)
11027 {
11028   if (!tape.use_mouse)
11029     return;
11030
11031   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11032   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11033   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11034 }
11035
11036 static void SetTapeActionFromMouseAction(byte *tape_action,
11037                                          struct MouseActionInfo *mouse_action)
11038 {
11039   if (!tape.use_mouse)
11040     return;
11041
11042   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11043   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11044   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11045 }
11046
11047 static void CheckLevelTime()
11048 {
11049   int i;
11050
11051   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11052   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11053   {
11054     if (level.native_em_level->lev->home == 0)  /* all players at home */
11055     {
11056       PlayerWins(local_player);
11057
11058       AllPlayersGone = TRUE;
11059
11060       level.native_em_level->lev->home = -1;
11061     }
11062
11063     if (level.native_em_level->ply[0]->alive == 0 &&
11064         level.native_em_level->ply[1]->alive == 0 &&
11065         level.native_em_level->ply[2]->alive == 0 &&
11066         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11067       AllPlayersGone = TRUE;
11068   }
11069   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11070   {
11071     if (game_sp.LevelSolved &&
11072         !game_sp.GameOver)                              /* game won */
11073     {
11074       PlayerWins(local_player);
11075
11076       game_sp.GameOver = TRUE;
11077
11078       AllPlayersGone = TRUE;
11079     }
11080
11081     if (game_sp.GameOver)                               /* game lost */
11082       AllPlayersGone = TRUE;
11083   }
11084   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11085   {
11086     if (game_mm.level_solved &&
11087         !game_mm.game_over)                             /* game won */
11088     {
11089       PlayerWins(local_player);
11090
11091       game_mm.game_over = TRUE;
11092
11093       AllPlayersGone = TRUE;
11094     }
11095
11096     if (game_mm.game_over)                              /* game lost */
11097       AllPlayersGone = TRUE;
11098   }
11099
11100   if (TimeFrames >= FRAMES_PER_SECOND)
11101   {
11102     TimeFrames = 0;
11103     TapeTime++;
11104
11105     for (i = 0; i < MAX_PLAYERS; i++)
11106     {
11107       struct PlayerInfo *player = &stored_player[i];
11108
11109       if (SHIELD_ON(player))
11110       {
11111         player->shield_normal_time_left--;
11112
11113         if (player->shield_deadly_time_left > 0)
11114           player->shield_deadly_time_left--;
11115       }
11116     }
11117
11118     if (!local_player->LevelSolved && !level.use_step_counter)
11119     {
11120       TimePlayed++;
11121
11122       if (TimeLeft > 0)
11123       {
11124         TimeLeft--;
11125
11126         if (TimeLeft <= 10 && setup.time_limit)
11127           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11128
11129         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11130            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11131
11132         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11133
11134         if (!TimeLeft && setup.time_limit)
11135         {
11136           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11137             level.native_em_level->lev->killed_out_of_time = TRUE;
11138           else
11139             for (i = 0; i < MAX_PLAYERS; i++)
11140               KillPlayer(&stored_player[i]);
11141         }
11142       }
11143       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11144       {
11145         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11146       }
11147
11148       level.native_em_level->lev->time =
11149         (game.no_time_limit ? TimePlayed : TimeLeft);
11150     }
11151
11152     if (tape.recording || tape.playing)
11153       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11154   }
11155
11156   if (tape.recording || tape.playing)
11157     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11158
11159   UpdateAndDisplayGameControlValues();
11160 }
11161
11162 void AdvanceFrameAndPlayerCounters(int player_nr)
11163 {
11164   int i;
11165
11166   /* advance frame counters (global frame counter and time frame counter) */
11167   FrameCounter++;
11168   TimeFrames++;
11169
11170   /* advance player counters (counters for move delay, move animation etc.) */
11171   for (i = 0; i < MAX_PLAYERS; i++)
11172   {
11173     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11174     int move_delay_value = stored_player[i].move_delay_value;
11175     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11176
11177     if (!advance_player_counters)       /* not all players may be affected */
11178       continue;
11179
11180     if (move_frames == 0)       /* less than one move per game frame */
11181     {
11182       int stepsize = TILEX / move_delay_value;
11183       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11184       int count = (stored_player[i].is_moving ?
11185                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11186
11187       if (count % delay == 0)
11188         move_frames = 1;
11189     }
11190
11191     stored_player[i].Frame += move_frames;
11192
11193     if (stored_player[i].MovPos != 0)
11194       stored_player[i].StepFrame += move_frames;
11195
11196     if (stored_player[i].move_delay > 0)
11197       stored_player[i].move_delay--;
11198
11199     /* due to bugs in previous versions, counter must count up, not down */
11200     if (stored_player[i].push_delay != -1)
11201       stored_player[i].push_delay++;
11202
11203     if (stored_player[i].drop_delay > 0)
11204       stored_player[i].drop_delay--;
11205
11206     if (stored_player[i].is_dropping_pressed)
11207       stored_player[i].drop_pressed_delay++;
11208   }
11209 }
11210
11211 void StartGameActions(boolean init_network_game, boolean record_tape,
11212                       int random_seed)
11213 {
11214   unsigned int new_random_seed = InitRND(random_seed);
11215
11216   if (record_tape)
11217     TapeStartRecording(new_random_seed);
11218
11219   if (init_network_game)
11220   {
11221     SendToServer_StartPlaying();
11222
11223     return;
11224   }
11225
11226   InitGame();
11227 }
11228
11229 void GameActionsExt()
11230 {
11231 #if 0
11232   static unsigned int game_frame_delay = 0;
11233 #endif
11234   unsigned int game_frame_delay_value;
11235   byte *recorded_player_action;
11236   byte summarized_player_action = 0;
11237   byte tape_action[MAX_PLAYERS];
11238   int i;
11239
11240   /* detect endless loops, caused by custom element programming */
11241   if (recursion_loop_detected && recursion_loop_depth == 0)
11242   {
11243     char *message = getStringCat3("Internal Error! Element ",
11244                                   EL_NAME(recursion_loop_element),
11245                                   " caused endless loop! Quit the game?");
11246
11247     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11248           EL_NAME(recursion_loop_element));
11249
11250     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11251
11252     recursion_loop_detected = FALSE;    /* if game should be continued */
11253
11254     free(message);
11255
11256     return;
11257   }
11258
11259   if (game.restart_level)
11260     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11261
11262   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11263   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11264   {
11265     if (level.native_em_level->lev->home == 0)  /* all players at home */
11266     {
11267       PlayerWins(local_player);
11268
11269       AllPlayersGone = TRUE;
11270
11271       level.native_em_level->lev->home = -1;
11272     }
11273
11274     if (level.native_em_level->ply[0]->alive == 0 &&
11275         level.native_em_level->ply[1]->alive == 0 &&
11276         level.native_em_level->ply[2]->alive == 0 &&
11277         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11278       AllPlayersGone = TRUE;
11279   }
11280   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11281   {
11282     if (game_sp.LevelSolved &&
11283         !game_sp.GameOver)                              /* game won */
11284     {
11285       PlayerWins(local_player);
11286
11287       game_sp.GameOver = TRUE;
11288
11289       AllPlayersGone = TRUE;
11290     }
11291
11292     if (game_sp.GameOver)                               /* game lost */
11293       AllPlayersGone = TRUE;
11294   }
11295   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11296   {
11297     if (game_mm.level_solved &&
11298         !game_mm.game_over)                             /* game won */
11299     {
11300       PlayerWins(local_player);
11301
11302       game_mm.game_over = TRUE;
11303
11304       AllPlayersGone = TRUE;
11305     }
11306
11307     if (game_mm.game_over)                              /* game lost */
11308       AllPlayersGone = TRUE;
11309   }
11310
11311   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11312     GameWon();
11313
11314   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11315     TapeStop();
11316
11317   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11318     return;
11319
11320   game_frame_delay_value =
11321     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11322
11323   if (tape.playing && tape.warp_forward && !tape.pausing)
11324     game_frame_delay_value = 0;
11325
11326   SetVideoFrameDelay(game_frame_delay_value);
11327
11328 #if 0
11329 #if 0
11330   /* ---------- main game synchronization point ---------- */
11331
11332   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11333
11334   printf("::: skip == %d\n", skip);
11335
11336 #else
11337   /* ---------- main game synchronization point ---------- */
11338
11339   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11340 #endif
11341 #endif
11342
11343   if (network_playing && !network_player_action_received)
11344   {
11345     /* try to get network player actions in time */
11346
11347     /* last chance to get network player actions without main loop delay */
11348     HandleNetworking();
11349
11350     /* game was quit by network peer */
11351     if (game_status != GAME_MODE_PLAYING)
11352       return;
11353
11354     if (!network_player_action_received)
11355       return;           /* failed to get network player actions in time */
11356
11357     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11358   }
11359
11360   if (tape.pausing)
11361     return;
11362
11363   /* at this point we know that we really continue executing the game */
11364
11365   network_player_action_received = FALSE;
11366
11367   /* when playing tape, read previously recorded player input from tape data */
11368   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11369
11370   local_player->effective_mouse_action = local_player->mouse_action;
11371
11372   if (recorded_player_action != NULL)
11373     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11374                                  recorded_player_action);
11375
11376   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11377   if (tape.pausing)
11378     return;
11379
11380   if (tape.set_centered_player)
11381   {
11382     game.centered_player_nr_next = tape.centered_player_nr_next;
11383     game.set_centered_player = TRUE;
11384   }
11385
11386   for (i = 0; i < MAX_PLAYERS; i++)
11387   {
11388     summarized_player_action |= stored_player[i].action;
11389
11390     if (!network_playing && (game.team_mode || tape.playing))
11391       stored_player[i].effective_action = stored_player[i].action;
11392   }
11393
11394   if (network_playing)
11395     SendToServer_MovePlayer(summarized_player_action);
11396
11397   // summarize all actions at local players mapped input device position
11398   // (this allows using different input devices in single player mode)
11399   if (!network.enabled && !game.team_mode)
11400     stored_player[map_player_action[local_player->index_nr]].effective_action =
11401       summarized_player_action;
11402
11403   if (tape.recording &&
11404       setup.team_mode &&
11405       setup.input_on_focus &&
11406       game.centered_player_nr != -1)
11407   {
11408     for (i = 0; i < MAX_PLAYERS; i++)
11409       stored_player[i].effective_action =
11410         (i == game.centered_player_nr ? summarized_player_action : 0);
11411   }
11412
11413   if (recorded_player_action != NULL)
11414     for (i = 0; i < MAX_PLAYERS; i++)
11415       stored_player[i].effective_action = recorded_player_action[i];
11416
11417   for (i = 0; i < MAX_PLAYERS; i++)
11418   {
11419     tape_action[i] = stored_player[i].effective_action;
11420
11421     /* (this may happen in the RND game engine if a player was not present on
11422        the playfield on level start, but appeared later from a custom element */
11423     if (setup.team_mode &&
11424         tape.recording &&
11425         tape_action[i] &&
11426         !tape.player_participates[i])
11427       tape.player_participates[i] = TRUE;
11428   }
11429
11430   SetTapeActionFromMouseAction(tape_action,
11431                                &local_player->effective_mouse_action);
11432
11433   /* only record actions from input devices, but not programmed actions */
11434   if (tape.recording)
11435     TapeRecordAction(tape_action);
11436
11437 #if USE_NEW_PLAYER_ASSIGNMENTS
11438   // !!! also map player actions in single player mode !!!
11439   // if (game.team_mode)
11440   if (1)
11441   {
11442     byte mapped_action[MAX_PLAYERS];
11443
11444 #if DEBUG_PLAYER_ACTIONS
11445     printf(":::");
11446     for (i = 0; i < MAX_PLAYERS; i++)
11447       printf(" %d, ", stored_player[i].effective_action);
11448 #endif
11449
11450     for (i = 0; i < MAX_PLAYERS; i++)
11451       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11452
11453     for (i = 0; i < MAX_PLAYERS; i++)
11454       stored_player[i].effective_action = mapped_action[i];
11455
11456 #if DEBUG_PLAYER_ACTIONS
11457     printf(" =>");
11458     for (i = 0; i < MAX_PLAYERS; i++)
11459       printf(" %d, ", stored_player[i].effective_action);
11460     printf("\n");
11461 #endif
11462   }
11463 #if DEBUG_PLAYER_ACTIONS
11464   else
11465   {
11466     printf(":::");
11467     for (i = 0; i < MAX_PLAYERS; i++)
11468       printf(" %d, ", stored_player[i].effective_action);
11469     printf("\n");
11470   }
11471 #endif
11472 #endif
11473
11474   for (i = 0; i < MAX_PLAYERS; i++)
11475   {
11476     // allow engine snapshot in case of changed movement attempt
11477     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11478         (stored_player[i].effective_action & KEY_MOTION))
11479       game.snapshot.changed_action = TRUE;
11480
11481     // allow engine snapshot in case of snapping/dropping attempt
11482     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11483         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11484       game.snapshot.changed_action = TRUE;
11485
11486     game.snapshot.last_action[i] = stored_player[i].effective_action;
11487   }
11488
11489   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11490   {
11491     GameActions_EM_Main();
11492   }
11493   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11494   {
11495     GameActions_SP_Main();
11496   }
11497   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11498   {
11499     GameActions_MM_Main();
11500   }
11501   else
11502   {
11503     GameActions_RND_Main();
11504   }
11505
11506   BlitScreenToBitmap(backbuffer);
11507
11508   CheckLevelTime();
11509
11510   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11511
11512   if (global.show_frames_per_second)
11513   {
11514     static unsigned int fps_counter = 0;
11515     static int fps_frames = 0;
11516     unsigned int fps_delay_ms = Counter() - fps_counter;
11517
11518     fps_frames++;
11519
11520     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11521     {
11522       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11523
11524       fps_frames = 0;
11525       fps_counter = Counter();
11526
11527       /* always draw FPS to screen after FPS value was updated */
11528       redraw_mask |= REDRAW_FPS;
11529     }
11530
11531     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11532     if (GetDrawDeactivationMask() == REDRAW_NONE)
11533       redraw_mask |= REDRAW_FPS;
11534   }
11535 }
11536
11537 static void GameActions_CheckSaveEngineSnapshot()
11538 {
11539   if (!game.snapshot.save_snapshot)
11540     return;
11541
11542   // clear flag for saving snapshot _before_ saving snapshot
11543   game.snapshot.save_snapshot = FALSE;
11544
11545   SaveEngineSnapshotToList();
11546 }
11547
11548 void GameActions()
11549 {
11550   GameActionsExt();
11551
11552   GameActions_CheckSaveEngineSnapshot();
11553 }
11554
11555 void GameActions_EM_Main()
11556 {
11557   byte effective_action[MAX_PLAYERS];
11558   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11559   int i;
11560
11561   for (i = 0; i < MAX_PLAYERS; i++)
11562     effective_action[i] = stored_player[i].effective_action;
11563
11564   GameActions_EM(effective_action, warp_mode);
11565 }
11566
11567 void GameActions_SP_Main()
11568 {
11569   byte effective_action[MAX_PLAYERS];
11570   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11571   int i;
11572
11573   for (i = 0; i < MAX_PLAYERS; i++)
11574     effective_action[i] = stored_player[i].effective_action;
11575
11576   GameActions_SP(effective_action, warp_mode);
11577
11578   for (i = 0; i < MAX_PLAYERS; i++)
11579   {
11580     if (stored_player[i].force_dropping)
11581       stored_player[i].action |= KEY_BUTTON_DROP;
11582
11583     stored_player[i].force_dropping = FALSE;
11584   }
11585 }
11586
11587 void GameActions_MM_Main()
11588 {
11589   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11590
11591   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11592 }
11593
11594 void GameActions_RND_Main()
11595 {
11596   GameActions_RND();
11597 }
11598
11599 void GameActions_RND()
11600 {
11601   int magic_wall_x = 0, magic_wall_y = 0;
11602   int i, x, y, element, graphic, last_gfx_frame;
11603
11604   InitPlayfieldScanModeVars();
11605
11606   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11607   {
11608     SCAN_PLAYFIELD(x, y)
11609     {
11610       ChangeCount[x][y] = 0;
11611       ChangeEvent[x][y] = -1;
11612     }
11613   }
11614
11615   if (game.set_centered_player)
11616   {
11617     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11618
11619     /* switching to "all players" only possible if all players fit to screen */
11620     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11621     {
11622       game.centered_player_nr_next = game.centered_player_nr;
11623       game.set_centered_player = FALSE;
11624     }
11625
11626     /* do not switch focus to non-existing (or non-active) player */
11627     if (game.centered_player_nr_next >= 0 &&
11628         !stored_player[game.centered_player_nr_next].active)
11629     {
11630       game.centered_player_nr_next = game.centered_player_nr;
11631       game.set_centered_player = FALSE;
11632     }
11633   }
11634
11635   if (game.set_centered_player &&
11636       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11637   {
11638     int sx, sy;
11639
11640     if (game.centered_player_nr_next == -1)
11641     {
11642       setScreenCenteredToAllPlayers(&sx, &sy);
11643     }
11644     else
11645     {
11646       sx = stored_player[game.centered_player_nr_next].jx;
11647       sy = stored_player[game.centered_player_nr_next].jy;
11648     }
11649
11650     game.centered_player_nr = game.centered_player_nr_next;
11651     game.set_centered_player = FALSE;
11652
11653     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11654     DrawGameDoorValues();
11655   }
11656
11657   for (i = 0; i < MAX_PLAYERS; i++)
11658   {
11659     int actual_player_action = stored_player[i].effective_action;
11660
11661 #if 1
11662     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11663        - rnd_equinox_tetrachloride 048
11664        - rnd_equinox_tetrachloride_ii 096
11665        - rnd_emanuel_schmieg 002
11666        - doctor_sloan_ww 001, 020
11667     */
11668     if (stored_player[i].MovPos == 0)
11669       CheckGravityMovement(&stored_player[i]);
11670 #endif
11671
11672     /* overwrite programmed action with tape action */
11673     if (stored_player[i].programmed_action)
11674       actual_player_action = stored_player[i].programmed_action;
11675
11676     PlayerActions(&stored_player[i], actual_player_action);
11677
11678     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11679   }
11680
11681   ScrollScreen(NULL, SCROLL_GO_ON);
11682
11683   /* for backwards compatibility, the following code emulates a fixed bug that
11684      occured when pushing elements (causing elements that just made their last
11685      pushing step to already (if possible) make their first falling step in the
11686      same game frame, which is bad); this code is also needed to use the famous
11687      "spring push bug" which is used in older levels and might be wanted to be
11688      used also in newer levels, but in this case the buggy pushing code is only
11689      affecting the "spring" element and no other elements */
11690
11691   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11692   {
11693     for (i = 0; i < MAX_PLAYERS; i++)
11694     {
11695       struct PlayerInfo *player = &stored_player[i];
11696       int x = player->jx;
11697       int y = player->jy;
11698
11699       if (player->active && player->is_pushing && player->is_moving &&
11700           IS_MOVING(x, y) &&
11701           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11702            Feld[x][y] == EL_SPRING))
11703       {
11704         ContinueMoving(x, y);
11705
11706         /* continue moving after pushing (this is actually a bug) */
11707         if (!IS_MOVING(x, y))
11708           Stop[x][y] = FALSE;
11709       }
11710     }
11711   }
11712
11713   SCAN_PLAYFIELD(x, y)
11714   {
11715     ChangeCount[x][y] = 0;
11716     ChangeEvent[x][y] = -1;
11717
11718     /* this must be handled before main playfield loop */
11719     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11720     {
11721       MovDelay[x][y]--;
11722       if (MovDelay[x][y] <= 0)
11723         RemoveField(x, y);
11724     }
11725
11726     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11727     {
11728       MovDelay[x][y]--;
11729       if (MovDelay[x][y] <= 0)
11730       {
11731         RemoveField(x, y);
11732         TEST_DrawLevelField(x, y);
11733
11734         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11735       }
11736     }
11737
11738 #if DEBUG
11739     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11740     {
11741       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11742       printf("GameActions(): This should never happen!\n");
11743
11744       ChangePage[x][y] = -1;
11745     }
11746 #endif
11747
11748     Stop[x][y] = FALSE;
11749     if (WasJustMoving[x][y] > 0)
11750       WasJustMoving[x][y]--;
11751     if (WasJustFalling[x][y] > 0)
11752       WasJustFalling[x][y]--;
11753     if (CheckCollision[x][y] > 0)
11754       CheckCollision[x][y]--;
11755     if (CheckImpact[x][y] > 0)
11756       CheckImpact[x][y]--;
11757
11758     GfxFrame[x][y]++;
11759
11760     /* reset finished pushing action (not done in ContinueMoving() to allow
11761        continuous pushing animation for elements with zero push delay) */
11762     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11763     {
11764       ResetGfxAnimation(x, y);
11765       TEST_DrawLevelField(x, y);
11766     }
11767
11768 #if DEBUG
11769     if (IS_BLOCKED(x, y))
11770     {
11771       int oldx, oldy;
11772
11773       Blocked2Moving(x, y, &oldx, &oldy);
11774       if (!IS_MOVING(oldx, oldy))
11775       {
11776         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11777         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11778         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11779         printf("GameActions(): This should never happen!\n");
11780       }
11781     }
11782 #endif
11783   }
11784
11785   SCAN_PLAYFIELD(x, y)
11786   {
11787     element = Feld[x][y];
11788     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11789     last_gfx_frame = GfxFrame[x][y];
11790
11791     ResetGfxFrame(x, y);
11792
11793     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11794       DrawLevelGraphicAnimation(x, y, graphic);
11795
11796     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11797         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11798       ResetRandomAnimationValue(x, y);
11799
11800     SetRandomAnimationValue(x, y);
11801
11802     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11803
11804     if (IS_INACTIVE(element))
11805     {
11806       if (IS_ANIMATED(graphic))
11807         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11808
11809       continue;
11810     }
11811
11812     /* this may take place after moving, so 'element' may have changed */
11813     if (IS_CHANGING(x, y) &&
11814         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11815     {
11816       int page = element_info[element].event_page_nr[CE_DELAY];
11817
11818       HandleElementChange(x, y, page);
11819
11820       element = Feld[x][y];
11821       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11822     }
11823
11824     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11825     {
11826       StartMoving(x, y);
11827
11828       element = Feld[x][y];
11829       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11830
11831       if (IS_ANIMATED(graphic) &&
11832           !IS_MOVING(x, y) &&
11833           !Stop[x][y])
11834         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11835
11836       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11837         TEST_DrawTwinkleOnField(x, y);
11838     }
11839     else if (element == EL_ACID)
11840     {
11841       if (!Stop[x][y])
11842         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11843     }
11844     else if ((element == EL_EXIT_OPEN ||
11845               element == EL_EM_EXIT_OPEN ||
11846               element == EL_SP_EXIT_OPEN ||
11847               element == EL_STEEL_EXIT_OPEN ||
11848               element == EL_EM_STEEL_EXIT_OPEN ||
11849               element == EL_SP_TERMINAL ||
11850               element == EL_SP_TERMINAL_ACTIVE ||
11851               element == EL_EXTRA_TIME ||
11852               element == EL_SHIELD_NORMAL ||
11853               element == EL_SHIELD_DEADLY) &&
11854              IS_ANIMATED(graphic))
11855       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11856     else if (IS_MOVING(x, y))
11857       ContinueMoving(x, y);
11858     else if (IS_ACTIVE_BOMB(element))
11859       CheckDynamite(x, y);
11860     else if (element == EL_AMOEBA_GROWING)
11861       AmoebeWaechst(x, y);
11862     else if (element == EL_AMOEBA_SHRINKING)
11863       AmoebaDisappearing(x, y);
11864
11865 #if !USE_NEW_AMOEBA_CODE
11866     else if (IS_AMOEBALIVE(element))
11867       AmoebeAbleger(x, y);
11868 #endif
11869
11870     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11871       Life(x, y);
11872     else if (element == EL_EXIT_CLOSED)
11873       CheckExit(x, y);
11874     else if (element == EL_EM_EXIT_CLOSED)
11875       CheckExitEM(x, y);
11876     else if (element == EL_STEEL_EXIT_CLOSED)
11877       CheckExitSteel(x, y);
11878     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11879       CheckExitSteelEM(x, y);
11880     else if (element == EL_SP_EXIT_CLOSED)
11881       CheckExitSP(x, y);
11882     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11883              element == EL_EXPANDABLE_STEELWALL_GROWING)
11884       MauerWaechst(x, y);
11885     else if (element == EL_EXPANDABLE_WALL ||
11886              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11887              element == EL_EXPANDABLE_WALL_VERTICAL ||
11888              element == EL_EXPANDABLE_WALL_ANY ||
11889              element == EL_BD_EXPANDABLE_WALL)
11890       MauerAbleger(x, y);
11891     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11892              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11893              element == EL_EXPANDABLE_STEELWALL_ANY)
11894       MauerAblegerStahl(x, y);
11895     else if (element == EL_FLAMES)
11896       CheckForDragon(x, y);
11897     else if (element == EL_EXPLOSION)
11898       ; /* drawing of correct explosion animation is handled separately */
11899     else if (element == EL_ELEMENT_SNAPPING ||
11900              element == EL_DIAGONAL_SHRINKING ||
11901              element == EL_DIAGONAL_GROWING)
11902     {
11903       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11904
11905       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11906     }
11907     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11908       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11909
11910     if (IS_BELT_ACTIVE(element))
11911       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11912
11913     if (game.magic_wall_active)
11914     {
11915       int jx = local_player->jx, jy = local_player->jy;
11916
11917       /* play the element sound at the position nearest to the player */
11918       if ((element == EL_MAGIC_WALL_FULL ||
11919            element == EL_MAGIC_WALL_ACTIVE ||
11920            element == EL_MAGIC_WALL_EMPTYING ||
11921            element == EL_BD_MAGIC_WALL_FULL ||
11922            element == EL_BD_MAGIC_WALL_ACTIVE ||
11923            element == EL_BD_MAGIC_WALL_EMPTYING ||
11924            element == EL_DC_MAGIC_WALL_FULL ||
11925            element == EL_DC_MAGIC_WALL_ACTIVE ||
11926            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11927           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11928       {
11929         magic_wall_x = x;
11930         magic_wall_y = y;
11931       }
11932     }
11933   }
11934
11935 #if USE_NEW_AMOEBA_CODE
11936   /* new experimental amoeba growth stuff */
11937   if (!(FrameCounter % 8))
11938   {
11939     static unsigned int random = 1684108901;
11940
11941     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11942     {
11943       x = RND(lev_fieldx);
11944       y = RND(lev_fieldy);
11945       element = Feld[x][y];
11946
11947       if (!IS_PLAYER(x,y) &&
11948           (element == EL_EMPTY ||
11949            CAN_GROW_INTO(element) ||
11950            element == EL_QUICKSAND_EMPTY ||
11951            element == EL_QUICKSAND_FAST_EMPTY ||
11952            element == EL_ACID_SPLASH_LEFT ||
11953            element == EL_ACID_SPLASH_RIGHT))
11954       {
11955         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11956             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11957             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11958             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11959           Feld[x][y] = EL_AMOEBA_DROP;
11960       }
11961
11962       random = random * 129 + 1;
11963     }
11964   }
11965 #endif
11966
11967   game.explosions_delayed = FALSE;
11968
11969   SCAN_PLAYFIELD(x, y)
11970   {
11971     element = Feld[x][y];
11972
11973     if (ExplodeField[x][y])
11974       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11975     else if (element == EL_EXPLOSION)
11976       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11977
11978     ExplodeField[x][y] = EX_TYPE_NONE;
11979   }
11980
11981   game.explosions_delayed = TRUE;
11982
11983   if (game.magic_wall_active)
11984   {
11985     if (!(game.magic_wall_time_left % 4))
11986     {
11987       int element = Feld[magic_wall_x][magic_wall_y];
11988
11989       if (element == EL_BD_MAGIC_WALL_FULL ||
11990           element == EL_BD_MAGIC_WALL_ACTIVE ||
11991           element == EL_BD_MAGIC_WALL_EMPTYING)
11992         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11993       else if (element == EL_DC_MAGIC_WALL_FULL ||
11994                element == EL_DC_MAGIC_WALL_ACTIVE ||
11995                element == EL_DC_MAGIC_WALL_EMPTYING)
11996         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11997       else
11998         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11999     }
12000
12001     if (game.magic_wall_time_left > 0)
12002     {
12003       game.magic_wall_time_left--;
12004
12005       if (!game.magic_wall_time_left)
12006       {
12007         SCAN_PLAYFIELD(x, y)
12008         {
12009           element = Feld[x][y];
12010
12011           if (element == EL_MAGIC_WALL_ACTIVE ||
12012               element == EL_MAGIC_WALL_FULL)
12013           {
12014             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12015             TEST_DrawLevelField(x, y);
12016           }
12017           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12018                    element == EL_BD_MAGIC_WALL_FULL)
12019           {
12020             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12021             TEST_DrawLevelField(x, y);
12022           }
12023           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12024                    element == EL_DC_MAGIC_WALL_FULL)
12025           {
12026             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12027             TEST_DrawLevelField(x, y);
12028           }
12029         }
12030
12031         game.magic_wall_active = FALSE;
12032       }
12033     }
12034   }
12035
12036   if (game.light_time_left > 0)
12037   {
12038     game.light_time_left--;
12039
12040     if (game.light_time_left == 0)
12041       RedrawAllLightSwitchesAndInvisibleElements();
12042   }
12043
12044   if (game.timegate_time_left > 0)
12045   {
12046     game.timegate_time_left--;
12047
12048     if (game.timegate_time_left == 0)
12049       CloseAllOpenTimegates();
12050   }
12051
12052   if (game.lenses_time_left > 0)
12053   {
12054     game.lenses_time_left--;
12055
12056     if (game.lenses_time_left == 0)
12057       RedrawAllInvisibleElementsForLenses();
12058   }
12059
12060   if (game.magnify_time_left > 0)
12061   {
12062     game.magnify_time_left--;
12063
12064     if (game.magnify_time_left == 0)
12065       RedrawAllInvisibleElementsForMagnifier();
12066   }
12067
12068   for (i = 0; i < MAX_PLAYERS; i++)
12069   {
12070     struct PlayerInfo *player = &stored_player[i];
12071
12072     if (SHIELD_ON(player))
12073     {
12074       if (player->shield_deadly_time_left)
12075         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12076       else if (player->shield_normal_time_left)
12077         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12078     }
12079   }
12080
12081 #if USE_DELAYED_GFX_REDRAW
12082   SCAN_PLAYFIELD(x, y)
12083   {
12084     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12085     {
12086       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12087          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12088
12089       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12090         DrawLevelField(x, y);
12091
12092       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12093         DrawLevelFieldCrumbled(x, y);
12094
12095       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12096         DrawLevelFieldCrumbledNeighbours(x, y);
12097
12098       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12099         DrawTwinkleOnField(x, y);
12100     }
12101
12102     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12103   }
12104 #endif
12105
12106   DrawAllPlayers();
12107   PlayAllPlayersSound();
12108
12109   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12110   {
12111     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12112
12113     local_player->show_envelope = 0;
12114   }
12115
12116   /* use random number generator in every frame to make it less predictable */
12117   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12118     RND(1);
12119 }
12120
12121 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12122 {
12123   int min_x = x, min_y = y, max_x = x, max_y = y;
12124   int i;
12125
12126   for (i = 0; i < MAX_PLAYERS; i++)
12127   {
12128     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12129
12130     if (!stored_player[i].active || &stored_player[i] == player)
12131       continue;
12132
12133     min_x = MIN(min_x, jx);
12134     min_y = MIN(min_y, jy);
12135     max_x = MAX(max_x, jx);
12136     max_y = MAX(max_y, jy);
12137   }
12138
12139   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12140 }
12141
12142 static boolean AllPlayersInVisibleScreen()
12143 {
12144   int i;
12145
12146   for (i = 0; i < MAX_PLAYERS; i++)
12147   {
12148     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12149
12150     if (!stored_player[i].active)
12151       continue;
12152
12153     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12154       return FALSE;
12155   }
12156
12157   return TRUE;
12158 }
12159
12160 void ScrollLevel(int dx, int dy)
12161 {
12162   int scroll_offset = 2 * TILEX_VAR;
12163   int x, y;
12164
12165   BlitBitmap(drawto_field, drawto_field,
12166              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12167              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12168              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12169              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12170              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12171              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12172
12173   if (dx != 0)
12174   {
12175     x = (dx == 1 ? BX1 : BX2);
12176     for (y = BY1; y <= BY2; y++)
12177       DrawScreenField(x, y);
12178   }
12179
12180   if (dy != 0)
12181   {
12182     y = (dy == 1 ? BY1 : BY2);
12183     for (x = BX1; x <= BX2; x++)
12184       DrawScreenField(x, y);
12185   }
12186
12187   redraw_mask |= REDRAW_FIELD;
12188 }
12189
12190 static boolean canFallDown(struct PlayerInfo *player)
12191 {
12192   int jx = player->jx, jy = player->jy;
12193
12194   return (IN_LEV_FIELD(jx, jy + 1) &&
12195           (IS_FREE(jx, jy + 1) ||
12196            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12197           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12198           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12199 }
12200
12201 static boolean canPassField(int x, int y, int move_dir)
12202 {
12203   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12204   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12205   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12206   int nextx = x + dx;
12207   int nexty = y + dy;
12208   int element = Feld[x][y];
12209
12210   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12211           !CAN_MOVE(element) &&
12212           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12213           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12214           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12215 }
12216
12217 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12218 {
12219   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12220   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12221   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12222   int newx = x + dx;
12223   int newy = y + dy;
12224
12225   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12226           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12227           (IS_DIGGABLE(Feld[newx][newy]) ||
12228            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12229            canPassField(newx, newy, move_dir)));
12230 }
12231
12232 static void CheckGravityMovement(struct PlayerInfo *player)
12233 {
12234   if (player->gravity && !player->programmed_action)
12235   {
12236     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12237     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12238     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12239     int jx = player->jx, jy = player->jy;
12240     boolean player_is_moving_to_valid_field =
12241       (!player_is_snapping &&
12242        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12243         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12244     boolean player_can_fall_down = canFallDown(player);
12245
12246     if (player_can_fall_down &&
12247         !player_is_moving_to_valid_field)
12248       player->programmed_action = MV_DOWN;
12249   }
12250 }
12251
12252 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12253 {
12254   return CheckGravityMovement(player);
12255
12256   if (player->gravity && !player->programmed_action)
12257   {
12258     int jx = player->jx, jy = player->jy;
12259     boolean field_under_player_is_free =
12260       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12261     boolean player_is_standing_on_valid_field =
12262       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12263        (IS_WALKABLE(Feld[jx][jy]) &&
12264         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12265
12266     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12267       player->programmed_action = MV_DOWN;
12268   }
12269 }
12270
12271 /*
12272   MovePlayerOneStep()
12273   -----------------------------------------------------------------------------
12274   dx, dy:               direction (non-diagonal) to try to move the player to
12275   real_dx, real_dy:     direction as read from input device (can be diagonal)
12276 */
12277
12278 boolean MovePlayerOneStep(struct PlayerInfo *player,
12279                           int dx, int dy, int real_dx, int real_dy)
12280 {
12281   int jx = player->jx, jy = player->jy;
12282   int new_jx = jx + dx, new_jy = jy + dy;
12283   int can_move;
12284   boolean player_can_move = !player->cannot_move;
12285
12286   if (!player->active || (!dx && !dy))
12287     return MP_NO_ACTION;
12288
12289   player->MovDir = (dx < 0 ? MV_LEFT :
12290                     dx > 0 ? MV_RIGHT :
12291                     dy < 0 ? MV_UP :
12292                     dy > 0 ? MV_DOWN :  MV_NONE);
12293
12294   if (!IN_LEV_FIELD(new_jx, new_jy))
12295     return MP_NO_ACTION;
12296
12297   if (!player_can_move)
12298   {
12299     if (player->MovPos == 0)
12300     {
12301       player->is_moving = FALSE;
12302       player->is_digging = FALSE;
12303       player->is_collecting = FALSE;
12304       player->is_snapping = FALSE;
12305       player->is_pushing = FALSE;
12306     }
12307   }
12308
12309   if (!network.enabled && game.centered_player_nr == -1 &&
12310       !AllPlayersInSight(player, new_jx, new_jy))
12311     return MP_NO_ACTION;
12312
12313   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12314   if (can_move != MP_MOVING)
12315     return can_move;
12316
12317   /* check if DigField() has caused relocation of the player */
12318   if (player->jx != jx || player->jy != jy)
12319     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12320
12321   StorePlayer[jx][jy] = 0;
12322   player->last_jx = jx;
12323   player->last_jy = jy;
12324   player->jx = new_jx;
12325   player->jy = new_jy;
12326   StorePlayer[new_jx][new_jy] = player->element_nr;
12327
12328   if (player->move_delay_value_next != -1)
12329   {
12330     player->move_delay_value = player->move_delay_value_next;
12331     player->move_delay_value_next = -1;
12332   }
12333
12334   player->MovPos =
12335     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12336
12337   player->step_counter++;
12338
12339   PlayerVisit[jx][jy] = FrameCounter;
12340
12341   player->is_moving = TRUE;
12342
12343 #if 1
12344   /* should better be called in MovePlayer(), but this breaks some tapes */
12345   ScrollPlayer(player, SCROLL_INIT);
12346 #endif
12347
12348   return MP_MOVING;
12349 }
12350
12351 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12352 {
12353   int jx = player->jx, jy = player->jy;
12354   int old_jx = jx, old_jy = jy;
12355   int moved = MP_NO_ACTION;
12356
12357   if (!player->active)
12358     return FALSE;
12359
12360   if (!dx && !dy)
12361   {
12362     if (player->MovPos == 0)
12363     {
12364       player->is_moving = FALSE;
12365       player->is_digging = FALSE;
12366       player->is_collecting = FALSE;
12367       player->is_snapping = FALSE;
12368       player->is_pushing = FALSE;
12369     }
12370
12371     return FALSE;
12372   }
12373
12374   if (player->move_delay > 0)
12375     return FALSE;
12376
12377   player->move_delay = -1;              /* set to "uninitialized" value */
12378
12379   /* store if player is automatically moved to next field */
12380   player->is_auto_moving = (player->programmed_action != MV_NONE);
12381
12382   /* remove the last programmed player action */
12383   player->programmed_action = 0;
12384
12385   if (player->MovPos)
12386   {
12387     /* should only happen if pre-1.2 tape recordings are played */
12388     /* this is only for backward compatibility */
12389
12390     int original_move_delay_value = player->move_delay_value;
12391
12392 #if DEBUG
12393     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12394            tape.counter);
12395 #endif
12396
12397     /* scroll remaining steps with finest movement resolution */
12398     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12399
12400     while (player->MovPos)
12401     {
12402       ScrollPlayer(player, SCROLL_GO_ON);
12403       ScrollScreen(NULL, SCROLL_GO_ON);
12404
12405       AdvanceFrameAndPlayerCounters(player->index_nr);
12406
12407       DrawAllPlayers();
12408       BackToFront_WithFrameDelay(0);
12409     }
12410
12411     player->move_delay_value = original_move_delay_value;
12412   }
12413
12414   player->is_active = FALSE;
12415
12416   if (player->last_move_dir & MV_HORIZONTAL)
12417   {
12418     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12419       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12420   }
12421   else
12422   {
12423     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12424       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12425   }
12426
12427   if (!moved && !player->is_active)
12428   {
12429     player->is_moving = FALSE;
12430     player->is_digging = FALSE;
12431     player->is_collecting = FALSE;
12432     player->is_snapping = FALSE;
12433     player->is_pushing = FALSE;
12434   }
12435
12436   jx = player->jx;
12437   jy = player->jy;
12438
12439   if (moved & MP_MOVING && !ScreenMovPos &&
12440       (player->index_nr == game.centered_player_nr ||
12441        game.centered_player_nr == -1))
12442   {
12443     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12444     int offset = game.scroll_delay_value;
12445
12446     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12447     {
12448       /* actual player has left the screen -- scroll in that direction */
12449       if (jx != old_jx)         /* player has moved horizontally */
12450         scroll_x += (jx - old_jx);
12451       else                      /* player has moved vertically */
12452         scroll_y += (jy - old_jy);
12453     }
12454     else
12455     {
12456       if (jx != old_jx)         /* player has moved horizontally */
12457       {
12458         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12459             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12460           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12461
12462         /* don't scroll over playfield boundaries */
12463         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12464           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12465
12466         /* don't scroll more than one field at a time */
12467         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12468
12469         /* don't scroll against the player's moving direction */
12470         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12471             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12472           scroll_x = old_scroll_x;
12473       }
12474       else                      /* player has moved vertically */
12475       {
12476         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12477             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12478           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12479
12480         /* don't scroll over playfield boundaries */
12481         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12482           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12483
12484         /* don't scroll more than one field at a time */
12485         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12486
12487         /* don't scroll against the player's moving direction */
12488         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12489             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12490           scroll_y = old_scroll_y;
12491       }
12492     }
12493
12494     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12495     {
12496       if (!network.enabled && game.centered_player_nr == -1 &&
12497           !AllPlayersInVisibleScreen())
12498       {
12499         scroll_x = old_scroll_x;
12500         scroll_y = old_scroll_y;
12501       }
12502       else
12503       {
12504         ScrollScreen(player, SCROLL_INIT);
12505         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12506       }
12507     }
12508   }
12509
12510   player->StepFrame = 0;
12511
12512   if (moved & MP_MOVING)
12513   {
12514     if (old_jx != jx && old_jy == jy)
12515       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12516     else if (old_jx == jx && old_jy != jy)
12517       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12518
12519     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12520
12521     player->last_move_dir = player->MovDir;
12522     player->is_moving = TRUE;
12523     player->is_snapping = FALSE;
12524     player->is_switching = FALSE;
12525     player->is_dropping = FALSE;
12526     player->is_dropping_pressed = FALSE;
12527     player->drop_pressed_delay = 0;
12528
12529 #if 0
12530     /* should better be called here than above, but this breaks some tapes */
12531     ScrollPlayer(player, SCROLL_INIT);
12532 #endif
12533   }
12534   else
12535   {
12536     CheckGravityMovementWhenNotMoving(player);
12537
12538     player->is_moving = FALSE;
12539
12540     /* at this point, the player is allowed to move, but cannot move right now
12541        (e.g. because of something blocking the way) -- ensure that the player
12542        is also allowed to move in the next frame (in old versions before 3.1.1,
12543        the player was forced to wait again for eight frames before next try) */
12544
12545     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12546       player->move_delay = 0;   /* allow direct movement in the next frame */
12547   }
12548
12549   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12550     player->move_delay = player->move_delay_value;
12551
12552   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12553   {
12554     TestIfPlayerTouchesBadThing(jx, jy);
12555     TestIfPlayerTouchesCustomElement(jx, jy);
12556   }
12557
12558   if (!player->active)
12559     RemovePlayer(player);
12560
12561   return moved;
12562 }
12563
12564 void ScrollPlayer(struct PlayerInfo *player, int mode)
12565 {
12566   int jx = player->jx, jy = player->jy;
12567   int last_jx = player->last_jx, last_jy = player->last_jy;
12568   int move_stepsize = TILEX / player->move_delay_value;
12569
12570   if (!player->active)
12571     return;
12572
12573   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12574     return;
12575
12576   if (mode == SCROLL_INIT)
12577   {
12578     player->actual_frame_counter = FrameCounter;
12579     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12580
12581     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12582         Feld[last_jx][last_jy] == EL_EMPTY)
12583     {
12584       int last_field_block_delay = 0;   /* start with no blocking at all */
12585       int block_delay_adjustment = player->block_delay_adjustment;
12586
12587       /* if player blocks last field, add delay for exactly one move */
12588       if (player->block_last_field)
12589       {
12590         last_field_block_delay += player->move_delay_value;
12591
12592         /* when blocking enabled, prevent moving up despite gravity */
12593         if (player->gravity && player->MovDir == MV_UP)
12594           block_delay_adjustment = -1;
12595       }
12596
12597       /* add block delay adjustment (also possible when not blocking) */
12598       last_field_block_delay += block_delay_adjustment;
12599
12600       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12601       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12602     }
12603
12604     if (player->MovPos != 0)    /* player has not yet reached destination */
12605       return;
12606   }
12607   else if (!FrameReached(&player->actual_frame_counter, 1))
12608     return;
12609
12610   if (player->MovPos != 0)
12611   {
12612     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12613     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12614
12615     /* before DrawPlayer() to draw correct player graphic for this case */
12616     if (player->MovPos == 0)
12617       CheckGravityMovement(player);
12618   }
12619
12620   if (player->MovPos == 0)      /* player reached destination field */
12621   {
12622     if (player->move_delay_reset_counter > 0)
12623     {
12624       player->move_delay_reset_counter--;
12625
12626       if (player->move_delay_reset_counter == 0)
12627       {
12628         /* continue with normal speed after quickly moving through gate */
12629         HALVE_PLAYER_SPEED(player);
12630
12631         /* be able to make the next move without delay */
12632         player->move_delay = 0;
12633       }
12634     }
12635
12636     player->last_jx = jx;
12637     player->last_jy = jy;
12638
12639     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12640         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12641         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12642         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12643         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12644         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12645         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12646         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12647     {
12648       ExitPlayer(player);
12649
12650       if ((local_player->friends_still_needed == 0 ||
12651            IS_SP_ELEMENT(Feld[jx][jy])) &&
12652           AllPlayersGone)
12653         PlayerWins(local_player);
12654     }
12655
12656     /* this breaks one level: "machine", level 000 */
12657     {
12658       int move_direction = player->MovDir;
12659       int enter_side = MV_DIR_OPPOSITE(move_direction);
12660       int leave_side = move_direction;
12661       int old_jx = last_jx;
12662       int old_jy = last_jy;
12663       int old_element = Feld[old_jx][old_jy];
12664       int new_element = Feld[jx][jy];
12665
12666       if (IS_CUSTOM_ELEMENT(old_element))
12667         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12668                                    CE_LEFT_BY_PLAYER,
12669                                    player->index_bit, leave_side);
12670
12671       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12672                                           CE_PLAYER_LEAVES_X,
12673                                           player->index_bit, leave_side);
12674
12675       if (IS_CUSTOM_ELEMENT(new_element))
12676         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12677                                    player->index_bit, enter_side);
12678
12679       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12680                                           CE_PLAYER_ENTERS_X,
12681                                           player->index_bit, enter_side);
12682
12683       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12684                                         CE_MOVE_OF_X, move_direction);
12685     }
12686
12687     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12688     {
12689       TestIfPlayerTouchesBadThing(jx, jy);
12690       TestIfPlayerTouchesCustomElement(jx, jy);
12691
12692       /* needed because pushed element has not yet reached its destination,
12693          so it would trigger a change event at its previous field location */
12694       if (!player->is_pushing)
12695         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12696
12697       if (!player->active)
12698         RemovePlayer(player);
12699     }
12700
12701     if (!local_player->LevelSolved && level.use_step_counter)
12702     {
12703       int i;
12704
12705       TimePlayed++;
12706
12707       if (TimeLeft > 0)
12708       {
12709         TimeLeft--;
12710
12711         if (TimeLeft <= 10 && setup.time_limit)
12712           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12713
12714         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12715
12716         DisplayGameControlValues();
12717
12718         if (!TimeLeft && setup.time_limit)
12719           for (i = 0; i < MAX_PLAYERS; i++)
12720             KillPlayer(&stored_player[i]);
12721       }
12722       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12723       {
12724         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12725
12726         DisplayGameControlValues();
12727       }
12728     }
12729
12730     if (tape.single_step && tape.recording && !tape.pausing &&
12731         !player->programmed_action)
12732       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12733
12734     if (!player->programmed_action)
12735       CheckSaveEngineSnapshot(player);
12736   }
12737 }
12738
12739 void ScrollScreen(struct PlayerInfo *player, int mode)
12740 {
12741   static unsigned int screen_frame_counter = 0;
12742
12743   if (mode == SCROLL_INIT)
12744   {
12745     /* set scrolling step size according to actual player's moving speed */
12746     ScrollStepSize = TILEX / player->move_delay_value;
12747
12748     screen_frame_counter = FrameCounter;
12749     ScreenMovDir = player->MovDir;
12750     ScreenMovPos = player->MovPos;
12751     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12752     return;
12753   }
12754   else if (!FrameReached(&screen_frame_counter, 1))
12755     return;
12756
12757   if (ScreenMovPos)
12758   {
12759     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12760     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12761     redraw_mask |= REDRAW_FIELD;
12762   }
12763   else
12764     ScreenMovDir = MV_NONE;
12765 }
12766
12767 void TestIfPlayerTouchesCustomElement(int x, int y)
12768 {
12769   static int xy[4][2] =
12770   {
12771     { 0, -1 },
12772     { -1, 0 },
12773     { +1, 0 },
12774     { 0, +1 }
12775   };
12776   static int trigger_sides[4][2] =
12777   {
12778     /* center side       border side */
12779     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12780     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12781     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12782     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12783   };
12784   static int touch_dir[4] =
12785   {
12786     MV_LEFT | MV_RIGHT,
12787     MV_UP   | MV_DOWN,
12788     MV_UP   | MV_DOWN,
12789     MV_LEFT | MV_RIGHT
12790   };
12791   int center_element = Feld[x][y];      /* should always be non-moving! */
12792   int i;
12793
12794   for (i = 0; i < NUM_DIRECTIONS; i++)
12795   {
12796     int xx = x + xy[i][0];
12797     int yy = y + xy[i][1];
12798     int center_side = trigger_sides[i][0];
12799     int border_side = trigger_sides[i][1];
12800     int border_element;
12801
12802     if (!IN_LEV_FIELD(xx, yy))
12803       continue;
12804
12805     if (IS_PLAYER(x, y))                /* player found at center element */
12806     {
12807       struct PlayerInfo *player = PLAYERINFO(x, y);
12808
12809       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12810         border_element = Feld[xx][yy];          /* may be moving! */
12811       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12812         border_element = Feld[xx][yy];
12813       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12814         border_element = MovingOrBlocked2Element(xx, yy);
12815       else
12816         continue;               /* center and border element do not touch */
12817
12818       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12819                                  player->index_bit, border_side);
12820       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12821                                           CE_PLAYER_TOUCHES_X,
12822                                           player->index_bit, border_side);
12823
12824       {
12825         /* use player element that is initially defined in the level playfield,
12826            not the player element that corresponds to the runtime player number
12827            (example: a level that contains EL_PLAYER_3 as the only player would
12828            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12829         int player_element = PLAYERINFO(x, y)->initial_element;
12830
12831         CheckElementChangeBySide(xx, yy, border_element, player_element,
12832                                  CE_TOUCHING_X, border_side);
12833       }
12834     }
12835     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12836     {
12837       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12838
12839       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12840       {
12841         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12842           continue;             /* center and border element do not touch */
12843       }
12844
12845       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12846                                  player->index_bit, center_side);
12847       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12848                                           CE_PLAYER_TOUCHES_X,
12849                                           player->index_bit, center_side);
12850
12851       {
12852         /* use player element that is initially defined in the level playfield,
12853            not the player element that corresponds to the runtime player number
12854            (example: a level that contains EL_PLAYER_3 as the only player would
12855            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12856         int player_element = PLAYERINFO(xx, yy)->initial_element;
12857
12858         CheckElementChangeBySide(x, y, center_element, player_element,
12859                                  CE_TOUCHING_X, center_side);
12860       }
12861
12862       break;
12863     }
12864   }
12865 }
12866
12867 void TestIfElementTouchesCustomElement(int x, int y)
12868 {
12869   static int xy[4][2] =
12870   {
12871     { 0, -1 },
12872     { -1, 0 },
12873     { +1, 0 },
12874     { 0, +1 }
12875   };
12876   static int trigger_sides[4][2] =
12877   {
12878     /* center side      border side */
12879     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12880     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12881     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12882     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12883   };
12884   static int touch_dir[4] =
12885   {
12886     MV_LEFT | MV_RIGHT,
12887     MV_UP   | MV_DOWN,
12888     MV_UP   | MV_DOWN,
12889     MV_LEFT | MV_RIGHT
12890   };
12891   boolean change_center_element = FALSE;
12892   int center_element = Feld[x][y];      /* should always be non-moving! */
12893   int border_element_old[NUM_DIRECTIONS];
12894   int i;
12895
12896   for (i = 0; i < NUM_DIRECTIONS; i++)
12897   {
12898     int xx = x + xy[i][0];
12899     int yy = y + xy[i][1];
12900     int border_element;
12901
12902     border_element_old[i] = -1;
12903
12904     if (!IN_LEV_FIELD(xx, yy))
12905       continue;
12906
12907     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12908       border_element = Feld[xx][yy];    /* may be moving! */
12909     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12910       border_element = Feld[xx][yy];
12911     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12912       border_element = MovingOrBlocked2Element(xx, yy);
12913     else
12914       continue;                 /* center and border element do not touch */
12915
12916     border_element_old[i] = border_element;
12917   }
12918
12919   for (i = 0; i < NUM_DIRECTIONS; i++)
12920   {
12921     int xx = x + xy[i][0];
12922     int yy = y + xy[i][1];
12923     int center_side = trigger_sides[i][0];
12924     int border_element = border_element_old[i];
12925
12926     if (border_element == -1)
12927       continue;
12928
12929     /* check for change of border element */
12930     CheckElementChangeBySide(xx, yy, border_element, center_element,
12931                              CE_TOUCHING_X, center_side);
12932
12933     /* (center element cannot be player, so we dont have to check this here) */
12934   }
12935
12936   for (i = 0; i < NUM_DIRECTIONS; i++)
12937   {
12938     int xx = x + xy[i][0];
12939     int yy = y + xy[i][1];
12940     int border_side = trigger_sides[i][1];
12941     int border_element = border_element_old[i];
12942
12943     if (border_element == -1)
12944       continue;
12945
12946     /* check for change of center element (but change it only once) */
12947     if (!change_center_element)
12948       change_center_element =
12949         CheckElementChangeBySide(x, y, center_element, border_element,
12950                                  CE_TOUCHING_X, border_side);
12951
12952     if (IS_PLAYER(xx, yy))
12953     {
12954       /* use player element that is initially defined in the level playfield,
12955          not the player element that corresponds to the runtime player number
12956          (example: a level that contains EL_PLAYER_3 as the only player would
12957          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12958       int player_element = PLAYERINFO(xx, yy)->initial_element;
12959
12960       CheckElementChangeBySide(x, y, center_element, player_element,
12961                                CE_TOUCHING_X, border_side);
12962     }
12963   }
12964 }
12965
12966 void TestIfElementHitsCustomElement(int x, int y, int direction)
12967 {
12968   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12969   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12970   int hitx = x + dx, hity = y + dy;
12971   int hitting_element = Feld[x][y];
12972   int touched_element;
12973
12974   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12975     return;
12976
12977   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12978                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12979
12980   if (IN_LEV_FIELD(hitx, hity))
12981   {
12982     int opposite_direction = MV_DIR_OPPOSITE(direction);
12983     int hitting_side = direction;
12984     int touched_side = opposite_direction;
12985     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12986                           MovDir[hitx][hity] != direction ||
12987                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12988
12989     object_hit = TRUE;
12990
12991     if (object_hit)
12992     {
12993       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12994                                CE_HITTING_X, touched_side);
12995
12996       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12997                                CE_HIT_BY_X, hitting_side);
12998
12999       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13000                                CE_HIT_BY_SOMETHING, opposite_direction);
13001
13002       if (IS_PLAYER(hitx, hity))
13003       {
13004         /* use player element that is initially defined in the level playfield,
13005            not the player element that corresponds to the runtime player number
13006            (example: a level that contains EL_PLAYER_3 as the only player would
13007            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13008         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13009
13010         CheckElementChangeBySide(x, y, hitting_element, player_element,
13011                                  CE_HITTING_X, touched_side);
13012       }
13013     }
13014   }
13015
13016   /* "hitting something" is also true when hitting the playfield border */
13017   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13018                            CE_HITTING_SOMETHING, direction);
13019 }
13020
13021 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13022 {
13023   int i, kill_x = -1, kill_y = -1;
13024
13025   int bad_element = -1;
13026   static int test_xy[4][2] =
13027   {
13028     { 0, -1 },
13029     { -1, 0 },
13030     { +1, 0 },
13031     { 0, +1 }
13032   };
13033   static int test_dir[4] =
13034   {
13035     MV_UP,
13036     MV_LEFT,
13037     MV_RIGHT,
13038     MV_DOWN
13039   };
13040
13041   for (i = 0; i < NUM_DIRECTIONS; i++)
13042   {
13043     int test_x, test_y, test_move_dir, test_element;
13044
13045     test_x = good_x + test_xy[i][0];
13046     test_y = good_y + test_xy[i][1];
13047
13048     if (!IN_LEV_FIELD(test_x, test_y))
13049       continue;
13050
13051     test_move_dir =
13052       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13053
13054     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13055
13056     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13057        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13058     */
13059     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13060         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13061     {
13062       kill_x = test_x;
13063       kill_y = test_y;
13064       bad_element = test_element;
13065
13066       break;
13067     }
13068   }
13069
13070   if (kill_x != -1 || kill_y != -1)
13071   {
13072     if (IS_PLAYER(good_x, good_y))
13073     {
13074       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13075
13076       if (player->shield_deadly_time_left > 0 &&
13077           !IS_INDESTRUCTIBLE(bad_element))
13078         Bang(kill_x, kill_y);
13079       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13080         KillPlayer(player);
13081     }
13082     else
13083       Bang(good_x, good_y);
13084   }
13085 }
13086
13087 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13088 {
13089   int i, kill_x = -1, kill_y = -1;
13090   int bad_element = Feld[bad_x][bad_y];
13091   static int test_xy[4][2] =
13092   {
13093     { 0, -1 },
13094     { -1, 0 },
13095     { +1, 0 },
13096     { 0, +1 }
13097   };
13098   static int touch_dir[4] =
13099   {
13100     MV_LEFT | MV_RIGHT,
13101     MV_UP   | MV_DOWN,
13102     MV_UP   | MV_DOWN,
13103     MV_LEFT | MV_RIGHT
13104   };
13105   static int test_dir[4] =
13106   {
13107     MV_UP,
13108     MV_LEFT,
13109     MV_RIGHT,
13110     MV_DOWN
13111   };
13112
13113   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13114     return;
13115
13116   for (i = 0; i < NUM_DIRECTIONS; i++)
13117   {
13118     int test_x, test_y, test_move_dir, test_element;
13119
13120     test_x = bad_x + test_xy[i][0];
13121     test_y = bad_y + test_xy[i][1];
13122
13123     if (!IN_LEV_FIELD(test_x, test_y))
13124       continue;
13125
13126     test_move_dir =
13127       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13128
13129     test_element = Feld[test_x][test_y];
13130
13131     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13132        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13133     */
13134     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13135         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13136     {
13137       /* good thing is player or penguin that does not move away */
13138       if (IS_PLAYER(test_x, test_y))
13139       {
13140         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13141
13142         if (bad_element == EL_ROBOT && player->is_moving)
13143           continue;     /* robot does not kill player if he is moving */
13144
13145         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13146         {
13147           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13148             continue;           /* center and border element do not touch */
13149         }
13150
13151         kill_x = test_x;
13152         kill_y = test_y;
13153
13154         break;
13155       }
13156       else if (test_element == EL_PENGUIN)
13157       {
13158         kill_x = test_x;
13159         kill_y = test_y;
13160
13161         break;
13162       }
13163     }
13164   }
13165
13166   if (kill_x != -1 || kill_y != -1)
13167   {
13168     if (IS_PLAYER(kill_x, kill_y))
13169     {
13170       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13171
13172       if (player->shield_deadly_time_left > 0 &&
13173           !IS_INDESTRUCTIBLE(bad_element))
13174         Bang(bad_x, bad_y);
13175       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13176         KillPlayer(player);
13177     }
13178     else
13179       Bang(kill_x, kill_y);
13180   }
13181 }
13182
13183 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13184 {
13185   int bad_element = Feld[bad_x][bad_y];
13186   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13187   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13188   int test_x = bad_x + dx, test_y = bad_y + dy;
13189   int test_move_dir, test_element;
13190   int kill_x = -1, kill_y = -1;
13191
13192   if (!IN_LEV_FIELD(test_x, test_y))
13193     return;
13194
13195   test_move_dir =
13196     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13197
13198   test_element = Feld[test_x][test_y];
13199
13200   if (test_move_dir != bad_move_dir)
13201   {
13202     /* good thing can be player or penguin that does not move away */
13203     if (IS_PLAYER(test_x, test_y))
13204     {
13205       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13206
13207       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13208          player as being hit when he is moving towards the bad thing, because
13209          the "get hit by" condition would be lost after the player stops) */
13210       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13211         return;         /* player moves away from bad thing */
13212
13213       kill_x = test_x;
13214       kill_y = test_y;
13215     }
13216     else if (test_element == EL_PENGUIN)
13217     {
13218       kill_x = test_x;
13219       kill_y = test_y;
13220     }
13221   }
13222
13223   if (kill_x != -1 || kill_y != -1)
13224   {
13225     if (IS_PLAYER(kill_x, kill_y))
13226     {
13227       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13228
13229       if (player->shield_deadly_time_left > 0 &&
13230           !IS_INDESTRUCTIBLE(bad_element))
13231         Bang(bad_x, bad_y);
13232       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13233         KillPlayer(player);
13234     }
13235     else
13236       Bang(kill_x, kill_y);
13237   }
13238 }
13239
13240 void TestIfPlayerTouchesBadThing(int x, int y)
13241 {
13242   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13243 }
13244
13245 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13246 {
13247   TestIfGoodThingHitsBadThing(x, y, move_dir);
13248 }
13249
13250 void TestIfBadThingTouchesPlayer(int x, int y)
13251 {
13252   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13253 }
13254
13255 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13256 {
13257   TestIfBadThingHitsGoodThing(x, y, move_dir);
13258 }
13259
13260 void TestIfFriendTouchesBadThing(int x, int y)
13261 {
13262   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13263 }
13264
13265 void TestIfBadThingTouchesFriend(int x, int y)
13266 {
13267   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13268 }
13269
13270 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13271 {
13272   int i, kill_x = bad_x, kill_y = bad_y;
13273   static int xy[4][2] =
13274   {
13275     { 0, -1 },
13276     { -1, 0 },
13277     { +1, 0 },
13278     { 0, +1 }
13279   };
13280
13281   for (i = 0; i < NUM_DIRECTIONS; i++)
13282   {
13283     int x, y, element;
13284
13285     x = bad_x + xy[i][0];
13286     y = bad_y + xy[i][1];
13287     if (!IN_LEV_FIELD(x, y))
13288       continue;
13289
13290     element = Feld[x][y];
13291     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13292         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13293     {
13294       kill_x = x;
13295       kill_y = y;
13296       break;
13297     }
13298   }
13299
13300   if (kill_x != bad_x || kill_y != bad_y)
13301     Bang(bad_x, bad_y);
13302 }
13303
13304 void KillPlayer(struct PlayerInfo *player)
13305 {
13306   int jx = player->jx, jy = player->jy;
13307
13308   if (!player->active)
13309     return;
13310
13311 #if 0
13312   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13313          player->killed, player->active, player->reanimated);
13314 #endif
13315
13316   /* the following code was introduced to prevent an infinite loop when calling
13317      -> Bang()
13318      -> CheckTriggeredElementChangeExt()
13319      -> ExecuteCustomElementAction()
13320      -> KillPlayer()
13321      -> (infinitely repeating the above sequence of function calls)
13322      which occurs when killing the player while having a CE with the setting
13323      "kill player X when explosion of <player X>"; the solution using a new
13324      field "player->killed" was chosen for backwards compatibility, although
13325      clever use of the fields "player->active" etc. would probably also work */
13326 #if 1
13327   if (player->killed)
13328     return;
13329 #endif
13330
13331   player->killed = TRUE;
13332
13333   /* remove accessible field at the player's position */
13334   Feld[jx][jy] = EL_EMPTY;
13335
13336   /* deactivate shield (else Bang()/Explode() would not work right) */
13337   player->shield_normal_time_left = 0;
13338   player->shield_deadly_time_left = 0;
13339
13340 #if 0
13341   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13342          player->killed, player->active, player->reanimated);
13343 #endif
13344
13345   Bang(jx, jy);
13346
13347 #if 0
13348   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13349          player->killed, player->active, player->reanimated);
13350 #endif
13351
13352   if (player->reanimated)       /* killed player may have been reanimated */
13353     player->killed = player->reanimated = FALSE;
13354   else
13355     BuryPlayer(player);
13356 }
13357
13358 static void KillPlayerUnlessEnemyProtected(int x, int y)
13359 {
13360   if (!PLAYER_ENEMY_PROTECTED(x, y))
13361     KillPlayer(PLAYERINFO(x, y));
13362 }
13363
13364 static void KillPlayerUnlessExplosionProtected(int x, int y)
13365 {
13366   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13367     KillPlayer(PLAYERINFO(x, y));
13368 }
13369
13370 void BuryPlayer(struct PlayerInfo *player)
13371 {
13372   int jx = player->jx, jy = player->jy;
13373
13374   if (!player->active)
13375     return;
13376
13377   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13378   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13379
13380   player->GameOver = TRUE;
13381   RemovePlayer(player);
13382 }
13383
13384 void RemovePlayer(struct PlayerInfo *player)
13385 {
13386   int jx = player->jx, jy = player->jy;
13387   int i, found = FALSE;
13388
13389   player->present = FALSE;
13390   player->active = FALSE;
13391
13392   if (!ExplodeField[jx][jy])
13393     StorePlayer[jx][jy] = 0;
13394
13395   if (player->is_moving)
13396     TEST_DrawLevelField(player->last_jx, player->last_jy);
13397
13398   for (i = 0; i < MAX_PLAYERS; i++)
13399     if (stored_player[i].active)
13400       found = TRUE;
13401
13402   if (!found)
13403     AllPlayersGone = TRUE;
13404
13405   ExitX = ZX = jx;
13406   ExitY = ZY = jy;
13407 }
13408
13409 void ExitPlayer(struct PlayerInfo *player)
13410 {
13411   DrawPlayer(player);   /* needed here only to cleanup last field */
13412   RemovePlayer(player);
13413 }
13414
13415 static void setFieldForSnapping(int x, int y, int element, int direction)
13416 {
13417   struct ElementInfo *ei = &element_info[element];
13418   int direction_bit = MV_DIR_TO_BIT(direction);
13419   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13420   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13421                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13422
13423   Feld[x][y] = EL_ELEMENT_SNAPPING;
13424   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13425
13426   ResetGfxAnimation(x, y);
13427
13428   GfxElement[x][y] = element;
13429   GfxAction[x][y] = action;
13430   GfxDir[x][y] = direction;
13431   GfxFrame[x][y] = -1;
13432 }
13433
13434 /*
13435   =============================================================================
13436   checkDiagonalPushing()
13437   -----------------------------------------------------------------------------
13438   check if diagonal input device direction results in pushing of object
13439   (by checking if the alternative direction is walkable, diggable, ...)
13440   =============================================================================
13441 */
13442
13443 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13444                                     int x, int y, int real_dx, int real_dy)
13445 {
13446   int jx, jy, dx, dy, xx, yy;
13447
13448   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13449     return TRUE;
13450
13451   /* diagonal direction: check alternative direction */
13452   jx = player->jx;
13453   jy = player->jy;
13454   dx = x - jx;
13455   dy = y - jy;
13456   xx = jx + (dx == 0 ? real_dx : 0);
13457   yy = jy + (dy == 0 ? real_dy : 0);
13458
13459   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13460 }
13461
13462 /*
13463   =============================================================================
13464   DigField()
13465   -----------------------------------------------------------------------------
13466   x, y:                 field next to player (non-diagonal) to try to dig to
13467   real_dx, real_dy:     direction as read from input device (can be diagonal)
13468   =============================================================================
13469 */
13470
13471 static int DigField(struct PlayerInfo *player,
13472                     int oldx, int oldy, int x, int y,
13473                     int real_dx, int real_dy, int mode)
13474 {
13475   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13476   boolean player_was_pushing = player->is_pushing;
13477   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13478   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13479   int jx = oldx, jy = oldy;
13480   int dx = x - jx, dy = y - jy;
13481   int nextx = x + dx, nexty = y + dy;
13482   int move_direction = (dx == -1 ? MV_LEFT  :
13483                         dx == +1 ? MV_RIGHT :
13484                         dy == -1 ? MV_UP    :
13485                         dy == +1 ? MV_DOWN  : MV_NONE);
13486   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13487   int dig_side = MV_DIR_OPPOSITE(move_direction);
13488   int old_element = Feld[jx][jy];
13489   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13490   int collect_count;
13491
13492   if (is_player)                /* function can also be called by EL_PENGUIN */
13493   {
13494     if (player->MovPos == 0)
13495     {
13496       player->is_digging = FALSE;
13497       player->is_collecting = FALSE;
13498     }
13499
13500     if (player->MovPos == 0)    /* last pushing move finished */
13501       player->is_pushing = FALSE;
13502
13503     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13504     {
13505       player->is_switching = FALSE;
13506       player->push_delay = -1;
13507
13508       return MP_NO_ACTION;
13509     }
13510   }
13511
13512   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13513     old_element = Back[jx][jy];
13514
13515   /* in case of element dropped at player position, check background */
13516   else if (Back[jx][jy] != EL_EMPTY &&
13517            game.engine_version >= VERSION_IDENT(2,2,0,0))
13518     old_element = Back[jx][jy];
13519
13520   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13521     return MP_NO_ACTION;        /* field has no opening in this direction */
13522
13523   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13524     return MP_NO_ACTION;        /* field has no opening in this direction */
13525
13526   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13527   {
13528     SplashAcid(x, y);
13529
13530     Feld[jx][jy] = player->artwork_element;
13531     InitMovingField(jx, jy, MV_DOWN);
13532     Store[jx][jy] = EL_ACID;
13533     ContinueMoving(jx, jy);
13534     BuryPlayer(player);
13535
13536     return MP_DONT_RUN_INTO;
13537   }
13538
13539   if (player_can_move && DONT_RUN_INTO(element))
13540   {
13541     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13542
13543     return MP_DONT_RUN_INTO;
13544   }
13545
13546   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13547     return MP_NO_ACTION;
13548
13549   collect_count = element_info[element].collect_count_initial;
13550
13551   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13552     return MP_NO_ACTION;
13553
13554   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13555     player_can_move = player_can_move_or_snap;
13556
13557   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13558       game.engine_version >= VERSION_IDENT(2,2,0,0))
13559   {
13560     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13561                                player->index_bit, dig_side);
13562     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13563                                         player->index_bit, dig_side);
13564
13565     if (element == EL_DC_LANDMINE)
13566       Bang(x, y);
13567
13568     if (Feld[x][y] != element)          /* field changed by snapping */
13569       return MP_ACTION;
13570
13571     return MP_NO_ACTION;
13572   }
13573
13574   if (player->gravity && is_player && !player->is_auto_moving &&
13575       canFallDown(player) && move_direction != MV_DOWN &&
13576       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13577     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13578
13579   if (player_can_move &&
13580       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13581   {
13582     int sound_element = SND_ELEMENT(element);
13583     int sound_action = ACTION_WALKING;
13584
13585     if (IS_RND_GATE(element))
13586     {
13587       if (!player->key[RND_GATE_NR(element)])
13588         return MP_NO_ACTION;
13589     }
13590     else if (IS_RND_GATE_GRAY(element))
13591     {
13592       if (!player->key[RND_GATE_GRAY_NR(element)])
13593         return MP_NO_ACTION;
13594     }
13595     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13596     {
13597       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13598         return MP_NO_ACTION;
13599     }
13600     else if (element == EL_EXIT_OPEN ||
13601              element == EL_EM_EXIT_OPEN ||
13602              element == EL_EM_EXIT_OPENING ||
13603              element == EL_STEEL_EXIT_OPEN ||
13604              element == EL_EM_STEEL_EXIT_OPEN ||
13605              element == EL_EM_STEEL_EXIT_OPENING ||
13606              element == EL_SP_EXIT_OPEN ||
13607              element == EL_SP_EXIT_OPENING)
13608     {
13609       sound_action = ACTION_PASSING;    /* player is passing exit */
13610     }
13611     else if (element == EL_EMPTY)
13612     {
13613       sound_action = ACTION_MOVING;             /* nothing to walk on */
13614     }
13615
13616     /* play sound from background or player, whatever is available */
13617     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13618       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13619     else
13620       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13621   }
13622   else if (player_can_move &&
13623            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13624   {
13625     if (!ACCESS_FROM(element, opposite_direction))
13626       return MP_NO_ACTION;      /* field not accessible from this direction */
13627
13628     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13629       return MP_NO_ACTION;
13630
13631     if (IS_EM_GATE(element))
13632     {
13633       if (!player->key[EM_GATE_NR(element)])
13634         return MP_NO_ACTION;
13635     }
13636     else if (IS_EM_GATE_GRAY(element))
13637     {
13638       if (!player->key[EM_GATE_GRAY_NR(element)])
13639         return MP_NO_ACTION;
13640     }
13641     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13642     {
13643       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13644         return MP_NO_ACTION;
13645     }
13646     else if (IS_EMC_GATE(element))
13647     {
13648       if (!player->key[EMC_GATE_NR(element)])
13649         return MP_NO_ACTION;
13650     }
13651     else if (IS_EMC_GATE_GRAY(element))
13652     {
13653       if (!player->key[EMC_GATE_GRAY_NR(element)])
13654         return MP_NO_ACTION;
13655     }
13656     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13657     {
13658       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13659         return MP_NO_ACTION;
13660     }
13661     else if (element == EL_DC_GATE_WHITE ||
13662              element == EL_DC_GATE_WHITE_GRAY ||
13663              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13664     {
13665       if (player->num_white_keys == 0)
13666         return MP_NO_ACTION;
13667
13668       player->num_white_keys--;
13669     }
13670     else if (IS_SP_PORT(element))
13671     {
13672       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13673           element == EL_SP_GRAVITY_PORT_RIGHT ||
13674           element == EL_SP_GRAVITY_PORT_UP ||
13675           element == EL_SP_GRAVITY_PORT_DOWN)
13676         player->gravity = !player->gravity;
13677       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13678                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13679                element == EL_SP_GRAVITY_ON_PORT_UP ||
13680                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13681         player->gravity = TRUE;
13682       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13683                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13684                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13685                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13686         player->gravity = FALSE;
13687     }
13688
13689     /* automatically move to the next field with double speed */
13690     player->programmed_action = move_direction;
13691
13692     if (player->move_delay_reset_counter == 0)
13693     {
13694       player->move_delay_reset_counter = 2;     /* two double speed steps */
13695
13696       DOUBLE_PLAYER_SPEED(player);
13697     }
13698
13699     PlayLevelSoundAction(x, y, ACTION_PASSING);
13700   }
13701   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13702   {
13703     RemoveField(x, y);
13704
13705     if (mode != DF_SNAP)
13706     {
13707       GfxElement[x][y] = GFX_ELEMENT(element);
13708       player->is_digging = TRUE;
13709     }
13710
13711     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13712
13713     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13714                                         player->index_bit, dig_side);
13715
13716     if (mode == DF_SNAP)
13717     {
13718       if (level.block_snap_field)
13719         setFieldForSnapping(x, y, element, move_direction);
13720       else
13721         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13722
13723       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13724                                           player->index_bit, dig_side);
13725     }
13726   }
13727   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13728   {
13729     RemoveField(x, y);
13730
13731     if (is_player && mode != DF_SNAP)
13732     {
13733       GfxElement[x][y] = element;
13734       player->is_collecting = TRUE;
13735     }
13736
13737     if (element == EL_SPEED_PILL)
13738     {
13739       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13740     }
13741     else if (element == EL_EXTRA_TIME && level.time > 0)
13742     {
13743       TimeLeft += level.extra_time;
13744
13745       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13746
13747       DisplayGameControlValues();
13748     }
13749     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13750     {
13751       player->shield_normal_time_left += level.shield_normal_time;
13752       if (element == EL_SHIELD_DEADLY)
13753         player->shield_deadly_time_left += level.shield_deadly_time;
13754     }
13755     else if (element == EL_DYNAMITE ||
13756              element == EL_EM_DYNAMITE ||
13757              element == EL_SP_DISK_RED)
13758     {
13759       if (player->inventory_size < MAX_INVENTORY_SIZE)
13760         player->inventory_element[player->inventory_size++] = element;
13761
13762       DrawGameDoorValues();
13763     }
13764     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13765     {
13766       player->dynabomb_count++;
13767       player->dynabombs_left++;
13768     }
13769     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13770     {
13771       player->dynabomb_size++;
13772     }
13773     else if (element == EL_DYNABOMB_INCREASE_POWER)
13774     {
13775       player->dynabomb_xl = TRUE;
13776     }
13777     else if (IS_KEY(element))
13778     {
13779       player->key[KEY_NR(element)] = TRUE;
13780
13781       DrawGameDoorValues();
13782     }
13783     else if (element == EL_DC_KEY_WHITE)
13784     {
13785       player->num_white_keys++;
13786
13787       /* display white keys? */
13788       /* DrawGameDoorValues(); */
13789     }
13790     else if (IS_ENVELOPE(element))
13791     {
13792       player->show_envelope = element;
13793     }
13794     else if (element == EL_EMC_LENSES)
13795     {
13796       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13797
13798       RedrawAllInvisibleElementsForLenses();
13799     }
13800     else if (element == EL_EMC_MAGNIFIER)
13801     {
13802       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13803
13804       RedrawAllInvisibleElementsForMagnifier();
13805     }
13806     else if (IS_DROPPABLE(element) ||
13807              IS_THROWABLE(element))     /* can be collected and dropped */
13808     {
13809       int i;
13810
13811       if (collect_count == 0)
13812         player->inventory_infinite_element = element;
13813       else
13814         for (i = 0; i < collect_count; i++)
13815           if (player->inventory_size < MAX_INVENTORY_SIZE)
13816             player->inventory_element[player->inventory_size++] = element;
13817
13818       DrawGameDoorValues();
13819     }
13820     else if (collect_count > 0)
13821     {
13822       local_player->gems_still_needed -= collect_count;
13823       if (local_player->gems_still_needed < 0)
13824         local_player->gems_still_needed = 0;
13825
13826       game.snapshot.collected_item = TRUE;
13827
13828       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13829
13830       DisplayGameControlValues();
13831     }
13832
13833     RaiseScoreElement(element);
13834     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13835
13836     if (is_player)
13837       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13838                                           player->index_bit, dig_side);
13839
13840     if (mode == DF_SNAP)
13841     {
13842       if (level.block_snap_field)
13843         setFieldForSnapping(x, y, element, move_direction);
13844       else
13845         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13846
13847       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13848                                           player->index_bit, dig_side);
13849     }
13850   }
13851   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13852   {
13853     if (mode == DF_SNAP && element != EL_BD_ROCK)
13854       return MP_NO_ACTION;
13855
13856     if (CAN_FALL(element) && dy)
13857       return MP_NO_ACTION;
13858
13859     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13860         !(element == EL_SPRING && level.use_spring_bug))
13861       return MP_NO_ACTION;
13862
13863     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13864         ((move_direction & MV_VERTICAL &&
13865           ((element_info[element].move_pattern & MV_LEFT &&
13866             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13867            (element_info[element].move_pattern & MV_RIGHT &&
13868             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13869          (move_direction & MV_HORIZONTAL &&
13870           ((element_info[element].move_pattern & MV_UP &&
13871             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13872            (element_info[element].move_pattern & MV_DOWN &&
13873             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13874       return MP_NO_ACTION;
13875
13876     /* do not push elements already moving away faster than player */
13877     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13878         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13879       return MP_NO_ACTION;
13880
13881     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13882     {
13883       if (player->push_delay_value == -1 || !player_was_pushing)
13884         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13885     }
13886     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13887     {
13888       if (player->push_delay_value == -1)
13889         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13890     }
13891     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13892     {
13893       if (!player->is_pushing)
13894         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13895     }
13896
13897     player->is_pushing = TRUE;
13898     player->is_active = TRUE;
13899
13900     if (!(IN_LEV_FIELD(nextx, nexty) &&
13901           (IS_FREE(nextx, nexty) ||
13902            (IS_SB_ELEMENT(element) &&
13903             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13904            (IS_CUSTOM_ELEMENT(element) &&
13905             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13906       return MP_NO_ACTION;
13907
13908     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13909       return MP_NO_ACTION;
13910
13911     if (player->push_delay == -1)       /* new pushing; restart delay */
13912       player->push_delay = 0;
13913
13914     if (player->push_delay < player->push_delay_value &&
13915         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13916         element != EL_SPRING && element != EL_BALLOON)
13917     {
13918       /* make sure that there is no move delay before next try to push */
13919       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13920         player->move_delay = 0;
13921
13922       return MP_NO_ACTION;
13923     }
13924
13925     if (IS_CUSTOM_ELEMENT(element) &&
13926         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13927     {
13928       if (!DigFieldByCE(nextx, nexty, element))
13929         return MP_NO_ACTION;
13930     }
13931
13932     if (IS_SB_ELEMENT(element))
13933     {
13934       if (element == EL_SOKOBAN_FIELD_FULL)
13935       {
13936         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13937         local_player->sokobanfields_still_needed++;
13938       }
13939
13940       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13941       {
13942         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13943         local_player->sokobanfields_still_needed--;
13944       }
13945
13946       Feld[x][y] = EL_SOKOBAN_OBJECT;
13947
13948       if (Back[x][y] == Back[nextx][nexty])
13949         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13950       else if (Back[x][y] != 0)
13951         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13952                                     ACTION_EMPTYING);
13953       else
13954         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13955                                     ACTION_FILLING);
13956
13957       if (local_player->sokobanfields_still_needed == 0 &&
13958           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13959       {
13960         PlayerWins(player);
13961
13962         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13963       }
13964     }
13965     else
13966       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13967
13968     InitMovingField(x, y, move_direction);
13969     GfxAction[x][y] = ACTION_PUSHING;
13970
13971     if (mode == DF_SNAP)
13972       ContinueMoving(x, y);
13973     else
13974       MovPos[x][y] = (dx != 0 ? dx : dy);
13975
13976     Pushed[x][y] = TRUE;
13977     Pushed[nextx][nexty] = TRUE;
13978
13979     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13980       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13981     else
13982       player->push_delay_value = -1;    /* get new value later */
13983
13984     /* check for element change _after_ element has been pushed */
13985     if (game.use_change_when_pushing_bug)
13986     {
13987       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13988                                  player->index_bit, dig_side);
13989       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13990                                           player->index_bit, dig_side);
13991     }
13992   }
13993   else if (IS_SWITCHABLE(element))
13994   {
13995     if (PLAYER_SWITCHING(player, x, y))
13996     {
13997       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13998                                           player->index_bit, dig_side);
13999
14000       return MP_ACTION;
14001     }
14002
14003     player->is_switching = TRUE;
14004     player->switch_x = x;
14005     player->switch_y = y;
14006
14007     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14008
14009     if (element == EL_ROBOT_WHEEL)
14010     {
14011       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14012       ZX = x;
14013       ZY = y;
14014
14015       game.robot_wheel_active = TRUE;
14016
14017       TEST_DrawLevelField(x, y);
14018     }
14019     else if (element == EL_SP_TERMINAL)
14020     {
14021       int xx, yy;
14022
14023       SCAN_PLAYFIELD(xx, yy)
14024       {
14025         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14026         {
14027           Bang(xx, yy);
14028         }
14029         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14030         {
14031           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14032
14033           ResetGfxAnimation(xx, yy);
14034           TEST_DrawLevelField(xx, yy);
14035         }
14036       }
14037     }
14038     else if (IS_BELT_SWITCH(element))
14039     {
14040       ToggleBeltSwitch(x, y);
14041     }
14042     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14043              element == EL_SWITCHGATE_SWITCH_DOWN ||
14044              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14045              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14046     {
14047       ToggleSwitchgateSwitch(x, y);
14048     }
14049     else if (element == EL_LIGHT_SWITCH ||
14050              element == EL_LIGHT_SWITCH_ACTIVE)
14051     {
14052       ToggleLightSwitch(x, y);
14053     }
14054     else if (element == EL_TIMEGATE_SWITCH ||
14055              element == EL_DC_TIMEGATE_SWITCH)
14056     {
14057       ActivateTimegateSwitch(x, y);
14058     }
14059     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14060              element == EL_BALLOON_SWITCH_RIGHT ||
14061              element == EL_BALLOON_SWITCH_UP    ||
14062              element == EL_BALLOON_SWITCH_DOWN  ||
14063              element == EL_BALLOON_SWITCH_NONE  ||
14064              element == EL_BALLOON_SWITCH_ANY)
14065     {
14066       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14067                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14068                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14069                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14070                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14071                              move_direction);
14072     }
14073     else if (element == EL_LAMP)
14074     {
14075       Feld[x][y] = EL_LAMP_ACTIVE;
14076       local_player->lights_still_needed--;
14077
14078       ResetGfxAnimation(x, y);
14079       TEST_DrawLevelField(x, y);
14080     }
14081     else if (element == EL_TIME_ORB_FULL)
14082     {
14083       Feld[x][y] = EL_TIME_ORB_EMPTY;
14084
14085       if (level.time > 0 || level.use_time_orb_bug)
14086       {
14087         TimeLeft += level.time_orb_time;
14088         game.no_time_limit = FALSE;
14089
14090         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14091
14092         DisplayGameControlValues();
14093       }
14094
14095       ResetGfxAnimation(x, y);
14096       TEST_DrawLevelField(x, y);
14097     }
14098     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14099              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14100     {
14101       int xx, yy;
14102
14103       game.ball_state = !game.ball_state;
14104
14105       SCAN_PLAYFIELD(xx, yy)
14106       {
14107         int e = Feld[xx][yy];
14108
14109         if (game.ball_state)
14110         {
14111           if (e == EL_EMC_MAGIC_BALL)
14112             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14113           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14114             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14115         }
14116         else
14117         {
14118           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14119             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14120           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14121             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14122         }
14123       }
14124     }
14125
14126     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14127                                         player->index_bit, dig_side);
14128
14129     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14130                                         player->index_bit, dig_side);
14131
14132     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14133                                         player->index_bit, dig_side);
14134
14135     return MP_ACTION;
14136   }
14137   else
14138   {
14139     if (!PLAYER_SWITCHING(player, x, y))
14140     {
14141       player->is_switching = TRUE;
14142       player->switch_x = x;
14143       player->switch_y = y;
14144
14145       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14146                                  player->index_bit, dig_side);
14147       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14148                                           player->index_bit, dig_side);
14149
14150       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14151                                  player->index_bit, dig_side);
14152       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14153                                           player->index_bit, dig_side);
14154     }
14155
14156     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14157                                player->index_bit, dig_side);
14158     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14159                                         player->index_bit, dig_side);
14160
14161     return MP_NO_ACTION;
14162   }
14163
14164   player->push_delay = -1;
14165
14166   if (is_player)                /* function can also be called by EL_PENGUIN */
14167   {
14168     if (Feld[x][y] != element)          /* really digged/collected something */
14169     {
14170       player->is_collecting = !player->is_digging;
14171       player->is_active = TRUE;
14172     }
14173   }
14174
14175   return MP_MOVING;
14176 }
14177
14178 static boolean DigFieldByCE(int x, int y, int digging_element)
14179 {
14180   int element = Feld[x][y];
14181
14182   if (!IS_FREE(x, y))
14183   {
14184     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14185                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14186                   ACTION_BREAKING);
14187
14188     /* no element can dig solid indestructible elements */
14189     if (IS_INDESTRUCTIBLE(element) &&
14190         !IS_DIGGABLE(element) &&
14191         !IS_COLLECTIBLE(element))
14192       return FALSE;
14193
14194     if (AmoebaNr[x][y] &&
14195         (element == EL_AMOEBA_FULL ||
14196          element == EL_BD_AMOEBA ||
14197          element == EL_AMOEBA_GROWING))
14198     {
14199       AmoebaCnt[AmoebaNr[x][y]]--;
14200       AmoebaCnt2[AmoebaNr[x][y]]--;
14201     }
14202
14203     if (IS_MOVING(x, y))
14204       RemoveMovingField(x, y);
14205     else
14206     {
14207       RemoveField(x, y);
14208       TEST_DrawLevelField(x, y);
14209     }
14210
14211     /* if digged element was about to explode, prevent the explosion */
14212     ExplodeField[x][y] = EX_TYPE_NONE;
14213
14214     PlayLevelSoundAction(x, y, action);
14215   }
14216
14217   Store[x][y] = EL_EMPTY;
14218
14219   /* this makes it possible to leave the removed element again */
14220   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14221     Store[x][y] = element;
14222
14223   return TRUE;
14224 }
14225
14226 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14227 {
14228   int jx = player->jx, jy = player->jy;
14229   int x = jx + dx, y = jy + dy;
14230   int snap_direction = (dx == -1 ? MV_LEFT  :
14231                         dx == +1 ? MV_RIGHT :
14232                         dy == -1 ? MV_UP    :
14233                         dy == +1 ? MV_DOWN  : MV_NONE);
14234   boolean can_continue_snapping = (level.continuous_snapping &&
14235                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14236
14237   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14238     return FALSE;
14239
14240   if (!player->active || !IN_LEV_FIELD(x, y))
14241     return FALSE;
14242
14243   if (dx && dy)
14244     return FALSE;
14245
14246   if (!dx && !dy)
14247   {
14248     if (player->MovPos == 0)
14249       player->is_pushing = FALSE;
14250
14251     player->is_snapping = FALSE;
14252
14253     if (player->MovPos == 0)
14254     {
14255       player->is_moving = FALSE;
14256       player->is_digging = FALSE;
14257       player->is_collecting = FALSE;
14258     }
14259
14260     return FALSE;
14261   }
14262
14263   /* prevent snapping with already pressed snap key when not allowed */
14264   if (player->is_snapping && !can_continue_snapping)
14265     return FALSE;
14266
14267   player->MovDir = snap_direction;
14268
14269   if (player->MovPos == 0)
14270   {
14271     player->is_moving = FALSE;
14272     player->is_digging = FALSE;
14273     player->is_collecting = FALSE;
14274   }
14275
14276   player->is_dropping = FALSE;
14277   player->is_dropping_pressed = FALSE;
14278   player->drop_pressed_delay = 0;
14279
14280   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14281     return FALSE;
14282
14283   player->is_snapping = TRUE;
14284   player->is_active = TRUE;
14285
14286   if (player->MovPos == 0)
14287   {
14288     player->is_moving = FALSE;
14289     player->is_digging = FALSE;
14290     player->is_collecting = FALSE;
14291   }
14292
14293   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14294     TEST_DrawLevelField(player->last_jx, player->last_jy);
14295
14296   TEST_DrawLevelField(x, y);
14297
14298   return TRUE;
14299 }
14300
14301 static boolean DropElement(struct PlayerInfo *player)
14302 {
14303   int old_element, new_element;
14304   int dropx = player->jx, dropy = player->jy;
14305   int drop_direction = player->MovDir;
14306   int drop_side = drop_direction;
14307   int drop_element = get_next_dropped_element(player);
14308
14309   /* do not drop an element on top of another element; when holding drop key
14310      pressed without moving, dropped element must move away before the next
14311      element can be dropped (this is especially important if the next element
14312      is dynamite, which can be placed on background for historical reasons) */
14313   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14314     return MP_ACTION;
14315
14316   if (IS_THROWABLE(drop_element))
14317   {
14318     dropx += GET_DX_FROM_DIR(drop_direction);
14319     dropy += GET_DY_FROM_DIR(drop_direction);
14320
14321     if (!IN_LEV_FIELD(dropx, dropy))
14322       return FALSE;
14323   }
14324
14325   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14326   new_element = drop_element;           /* default: no change when dropping */
14327
14328   /* check if player is active, not moving and ready to drop */
14329   if (!player->active || player->MovPos || player->drop_delay > 0)
14330     return FALSE;
14331
14332   /* check if player has anything that can be dropped */
14333   if (new_element == EL_UNDEFINED)
14334     return FALSE;
14335
14336   /* only set if player has anything that can be dropped */
14337   player->is_dropping_pressed = TRUE;
14338
14339   /* check if drop key was pressed long enough for EM style dynamite */
14340   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14341     return FALSE;
14342
14343   /* check if anything can be dropped at the current position */
14344   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14345     return FALSE;
14346
14347   /* collected custom elements can only be dropped on empty fields */
14348   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14349     return FALSE;
14350
14351   if (old_element != EL_EMPTY)
14352     Back[dropx][dropy] = old_element;   /* store old element on this field */
14353
14354   ResetGfxAnimation(dropx, dropy);
14355   ResetRandomAnimationValue(dropx, dropy);
14356
14357   if (player->inventory_size > 0 ||
14358       player->inventory_infinite_element != EL_UNDEFINED)
14359   {
14360     if (player->inventory_size > 0)
14361     {
14362       player->inventory_size--;
14363
14364       DrawGameDoorValues();
14365
14366       if (new_element == EL_DYNAMITE)
14367         new_element = EL_DYNAMITE_ACTIVE;
14368       else if (new_element == EL_EM_DYNAMITE)
14369         new_element = EL_EM_DYNAMITE_ACTIVE;
14370       else if (new_element == EL_SP_DISK_RED)
14371         new_element = EL_SP_DISK_RED_ACTIVE;
14372     }
14373
14374     Feld[dropx][dropy] = new_element;
14375
14376     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14377       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14378                           el2img(Feld[dropx][dropy]), 0);
14379
14380     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14381
14382     /* needed if previous element just changed to "empty" in the last frame */
14383     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14384
14385     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14386                                player->index_bit, drop_side);
14387     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14388                                         CE_PLAYER_DROPS_X,
14389                                         player->index_bit, drop_side);
14390
14391     TestIfElementTouchesCustomElement(dropx, dropy);
14392   }
14393   else          /* player is dropping a dyna bomb */
14394   {
14395     player->dynabombs_left--;
14396
14397     Feld[dropx][dropy] = new_element;
14398
14399     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14400       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14401                           el2img(Feld[dropx][dropy]), 0);
14402
14403     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14404   }
14405
14406   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14407     InitField_WithBug1(dropx, dropy, FALSE);
14408
14409   new_element = Feld[dropx][dropy];     /* element might have changed */
14410
14411   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14412       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14413   {
14414     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14415       MovDir[dropx][dropy] = drop_direction;
14416
14417     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14418
14419     /* do not cause impact style collision by dropping elements that can fall */
14420     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14421   }
14422
14423   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14424   player->is_dropping = TRUE;
14425
14426   player->drop_pressed_delay = 0;
14427   player->is_dropping_pressed = FALSE;
14428
14429   player->drop_x = dropx;
14430   player->drop_y = dropy;
14431
14432   return TRUE;
14433 }
14434
14435 /* ------------------------------------------------------------------------- */
14436 /* game sound playing functions                                              */
14437 /* ------------------------------------------------------------------------- */
14438
14439 static int *loop_sound_frame = NULL;
14440 static int *loop_sound_volume = NULL;
14441
14442 void InitPlayLevelSound()
14443 {
14444   int num_sounds = getSoundListSize();
14445
14446   checked_free(loop_sound_frame);
14447   checked_free(loop_sound_volume);
14448
14449   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14450   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14451 }
14452
14453 static void PlayLevelSound(int x, int y, int nr)
14454 {
14455   int sx = SCREENX(x), sy = SCREENY(y);
14456   int volume, stereo_position;
14457   int max_distance = 8;
14458   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14459
14460   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14461       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14462     return;
14463
14464   if (!IN_LEV_FIELD(x, y) ||
14465       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14466       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14467     return;
14468
14469   volume = SOUND_MAX_VOLUME;
14470
14471   if (!IN_SCR_FIELD(sx, sy))
14472   {
14473     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14474     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14475
14476     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14477   }
14478
14479   stereo_position = (SOUND_MAX_LEFT +
14480                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14481                      (SCR_FIELDX + 2 * max_distance));
14482
14483   if (IS_LOOP_SOUND(nr))
14484   {
14485     /* This assures that quieter loop sounds do not overwrite louder ones,
14486        while restarting sound volume comparison with each new game frame. */
14487
14488     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14489       return;
14490
14491     loop_sound_volume[nr] = volume;
14492     loop_sound_frame[nr] = FrameCounter;
14493   }
14494
14495   PlaySoundExt(nr, volume, stereo_position, type);
14496 }
14497
14498 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14499 {
14500   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14501                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14502                  y < LEVELY(BY1) ? LEVELY(BY1) :
14503                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14504                  sound_action);
14505 }
14506
14507 static void PlayLevelSoundAction(int x, int y, int action)
14508 {
14509   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14510 }
14511
14512 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14513 {
14514   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14515
14516   if (sound_effect != SND_UNDEFINED)
14517     PlayLevelSound(x, y, sound_effect);
14518 }
14519
14520 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14521                                               int action)
14522 {
14523   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14524
14525   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14526     PlayLevelSound(x, y, sound_effect);
14527 }
14528
14529 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14530 {
14531   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14532
14533   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14534     PlayLevelSound(x, y, sound_effect);
14535 }
14536
14537 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14538 {
14539   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14540
14541   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14542     StopSound(sound_effect);
14543 }
14544
14545 static int getLevelMusicNr()
14546 {
14547   if (levelset.music[level_nr] != MUS_UNDEFINED)
14548     return levelset.music[level_nr];            /* from config file */
14549   else
14550     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14551 }
14552
14553 static void FadeLevelSounds()
14554 {
14555   FadeSounds();
14556 }
14557
14558 static void FadeLevelMusic()
14559 {
14560   int music_nr = getLevelMusicNr();
14561   char *curr_music = getCurrentlyPlayingMusicFilename();
14562   char *next_music = getMusicInfoEntryFilename(music_nr);
14563
14564   if (!strEqual(curr_music, next_music))
14565     FadeMusic();
14566 }
14567
14568 void FadeLevelSoundsAndMusic()
14569 {
14570   FadeLevelSounds();
14571   FadeLevelMusic();
14572 }
14573
14574 static void PlayLevelMusic()
14575 {
14576   int music_nr = getLevelMusicNr();
14577   char *curr_music = getCurrentlyPlayingMusicFilename();
14578   char *next_music = getMusicInfoEntryFilename(music_nr);
14579
14580   if (!strEqual(curr_music, next_music))
14581     PlayMusic(music_nr);
14582 }
14583
14584 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14585 {
14586   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14587   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14588   int x = xx - 1 - offset;
14589   int y = yy - 1 - offset;
14590
14591   switch (sample)
14592   {
14593     case SAMPLE_blank:
14594       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14595       break;
14596
14597     case SAMPLE_roll:
14598       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14599       break;
14600
14601     case SAMPLE_stone:
14602       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14603       break;
14604
14605     case SAMPLE_nut:
14606       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14607       break;
14608
14609     case SAMPLE_crack:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14611       break;
14612
14613     case SAMPLE_bug:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14615       break;
14616
14617     case SAMPLE_tank:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14619       break;
14620
14621     case SAMPLE_android_clone:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14623       break;
14624
14625     case SAMPLE_android_move:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14627       break;
14628
14629     case SAMPLE_spring:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14631       break;
14632
14633     case SAMPLE_slurp:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14635       break;
14636
14637     case SAMPLE_eater:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14639       break;
14640
14641     case SAMPLE_eater_eat:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14643       break;
14644
14645     case SAMPLE_alien:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14647       break;
14648
14649     case SAMPLE_collect:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14651       break;
14652
14653     case SAMPLE_diamond:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14655       break;
14656
14657     case SAMPLE_squash:
14658       /* !!! CHECK THIS !!! */
14659 #if 1
14660       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14661 #else
14662       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14663 #endif
14664       break;
14665
14666     case SAMPLE_wonderfall:
14667       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14668       break;
14669
14670     case SAMPLE_drip:
14671       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14672       break;
14673
14674     case SAMPLE_push:
14675       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14676       break;
14677
14678     case SAMPLE_dirt:
14679       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14680       break;
14681
14682     case SAMPLE_acid:
14683       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14684       break;
14685
14686     case SAMPLE_ball:
14687       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14688       break;
14689
14690     case SAMPLE_grow:
14691       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14692       break;
14693
14694     case SAMPLE_wonder:
14695       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14696       break;
14697
14698     case SAMPLE_door:
14699       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14700       break;
14701
14702     case SAMPLE_exit_open:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14704       break;
14705
14706     case SAMPLE_exit_leave:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14708       break;
14709
14710     case SAMPLE_dynamite:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14712       break;
14713
14714     case SAMPLE_tick:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14716       break;
14717
14718     case SAMPLE_press:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14720       break;
14721
14722     case SAMPLE_wheel:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14724       break;
14725
14726     case SAMPLE_boom:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14728       break;
14729
14730     case SAMPLE_die:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14732       break;
14733
14734     case SAMPLE_time:
14735       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14736       break;
14737
14738     default:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14740       break;
14741   }
14742 }
14743
14744 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14745 {
14746   int element = map_element_SP_to_RND(element_sp);
14747   int action = map_action_SP_to_RND(action_sp);
14748   int offset = (setup.sp_show_border_elements ? 0 : 1);
14749   int x = xx - offset;
14750   int y = yy - offset;
14751
14752   PlayLevelSoundElementAction(x, y, element, action);
14753 }
14754
14755 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14756 {
14757   int element = map_element_MM_to_RND(element_mm);
14758   int action = map_action_MM_to_RND(action_mm);
14759   int offset = 0;
14760   int x = xx - offset;
14761   int y = yy - offset;
14762
14763   if (!IS_MM_ELEMENT(element))
14764     element = EL_MM_DEFAULT;
14765
14766   PlayLevelSoundElementAction(x, y, element, action);
14767 }
14768
14769 void PlaySound_MM(int sound_mm)
14770 {
14771   int sound = map_sound_MM_to_RND(sound_mm);
14772
14773   if (sound == SND_UNDEFINED)
14774     return;
14775
14776   PlaySound(sound);
14777 }
14778
14779 void PlaySoundLoop_MM(int sound_mm)
14780 {
14781   int sound = map_sound_MM_to_RND(sound_mm);
14782
14783   if (sound == SND_UNDEFINED)
14784     return;
14785
14786   PlaySoundLoop(sound);
14787 }
14788
14789 void StopSound_MM(int sound_mm)
14790 {
14791   int sound = map_sound_MM_to_RND(sound_mm);
14792
14793   if (sound == SND_UNDEFINED)
14794     return;
14795
14796   StopSound(sound);
14797 }
14798
14799 void RaiseScore(int value)
14800 {
14801   local_player->score += value;
14802
14803   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14804
14805   DisplayGameControlValues();
14806 }
14807
14808 void RaiseScoreElement(int element)
14809 {
14810   switch (element)
14811   {
14812     case EL_EMERALD:
14813     case EL_BD_DIAMOND:
14814     case EL_EMERALD_YELLOW:
14815     case EL_EMERALD_RED:
14816     case EL_EMERALD_PURPLE:
14817     case EL_SP_INFOTRON:
14818       RaiseScore(level.score[SC_EMERALD]);
14819       break;
14820     case EL_DIAMOND:
14821       RaiseScore(level.score[SC_DIAMOND]);
14822       break;
14823     case EL_CRYSTAL:
14824       RaiseScore(level.score[SC_CRYSTAL]);
14825       break;
14826     case EL_PEARL:
14827       RaiseScore(level.score[SC_PEARL]);
14828       break;
14829     case EL_BUG:
14830     case EL_BD_BUTTERFLY:
14831     case EL_SP_ELECTRON:
14832       RaiseScore(level.score[SC_BUG]);
14833       break;
14834     case EL_SPACESHIP:
14835     case EL_BD_FIREFLY:
14836     case EL_SP_SNIKSNAK:
14837       RaiseScore(level.score[SC_SPACESHIP]);
14838       break;
14839     case EL_YAMYAM:
14840     case EL_DARK_YAMYAM:
14841       RaiseScore(level.score[SC_YAMYAM]);
14842       break;
14843     case EL_ROBOT:
14844       RaiseScore(level.score[SC_ROBOT]);
14845       break;
14846     case EL_PACMAN:
14847       RaiseScore(level.score[SC_PACMAN]);
14848       break;
14849     case EL_NUT:
14850       RaiseScore(level.score[SC_NUT]);
14851       break;
14852     case EL_DYNAMITE:
14853     case EL_EM_DYNAMITE:
14854     case EL_SP_DISK_RED:
14855     case EL_DYNABOMB_INCREASE_NUMBER:
14856     case EL_DYNABOMB_INCREASE_SIZE:
14857     case EL_DYNABOMB_INCREASE_POWER:
14858       RaiseScore(level.score[SC_DYNAMITE]);
14859       break;
14860     case EL_SHIELD_NORMAL:
14861     case EL_SHIELD_DEADLY:
14862       RaiseScore(level.score[SC_SHIELD]);
14863       break;
14864     case EL_EXTRA_TIME:
14865       RaiseScore(level.extra_time_score);
14866       break;
14867     case EL_KEY_1:
14868     case EL_KEY_2:
14869     case EL_KEY_3:
14870     case EL_KEY_4:
14871     case EL_EM_KEY_1:
14872     case EL_EM_KEY_2:
14873     case EL_EM_KEY_3:
14874     case EL_EM_KEY_4:
14875     case EL_EMC_KEY_5:
14876     case EL_EMC_KEY_6:
14877     case EL_EMC_KEY_7:
14878     case EL_EMC_KEY_8:
14879     case EL_DC_KEY_WHITE:
14880       RaiseScore(level.score[SC_KEY]);
14881       break;
14882     default:
14883       RaiseScore(element_info[element].collect_score);
14884       break;
14885   }
14886 }
14887
14888 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14889 {
14890   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14891   {
14892     /* closing door required in case of envelope style request dialogs */
14893     if (!skip_request)
14894       CloseDoor(DOOR_CLOSE_1);
14895
14896     if (network.enabled)
14897       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14898     else
14899     {
14900       if (quick_quit)
14901         FadeSkipNextFadeIn();
14902
14903       SetGameStatus(GAME_MODE_MAIN);
14904
14905       DrawMainMenu();
14906     }
14907   }
14908   else          /* continue playing the game */
14909   {
14910     if (tape.playing && tape.deactivate_display)
14911       TapeDeactivateDisplayOff(TRUE);
14912
14913     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14914
14915     if (tape.playing && tape.deactivate_display)
14916       TapeDeactivateDisplayOn();
14917   }
14918 }
14919
14920 void RequestQuitGame(boolean ask_if_really_quit)
14921 {
14922   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14923   boolean skip_request = AllPlayersGone || quick_quit;
14924
14925   RequestQuitGameExt(skip_request, quick_quit,
14926                      "Do you really want to quit the game?");
14927 }
14928
14929 void RequestRestartGame(char *message)
14930 {
14931   game.restart_game_message = NULL;
14932
14933   if (Request(message, REQ_ASK | REQ_STAY_CLOSED))
14934   {
14935     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
14936   }
14937   else
14938   {
14939     SetGameStatus(GAME_MODE_MAIN);
14940
14941     DrawMainMenu();
14942   }
14943 }
14944
14945
14946 /* ------------------------------------------------------------------------- */
14947 /* random generator functions                                                */
14948 /* ------------------------------------------------------------------------- */
14949
14950 unsigned int InitEngineRandom_RND(int seed)
14951 {
14952   game.num_random_calls = 0;
14953
14954   return InitEngineRandom(seed);
14955 }
14956
14957 unsigned int RND(int max)
14958 {
14959   if (max > 0)
14960   {
14961     game.num_random_calls++;
14962
14963     return GetEngineRandom(max);
14964   }
14965
14966   return 0;
14967 }
14968
14969
14970 /* ------------------------------------------------------------------------- */
14971 /* game engine snapshot handling functions                                   */
14972 /* ------------------------------------------------------------------------- */
14973
14974 struct EngineSnapshotInfo
14975 {
14976   /* runtime values for custom element collect score */
14977   int collect_score[NUM_CUSTOM_ELEMENTS];
14978
14979   /* runtime values for group element choice position */
14980   int choice_pos[NUM_GROUP_ELEMENTS];
14981
14982   /* runtime values for belt position animations */
14983   int belt_graphic[4][NUM_BELT_PARTS];
14984   int belt_anim_mode[4][NUM_BELT_PARTS];
14985 };
14986
14987 static struct EngineSnapshotInfo engine_snapshot_rnd;
14988 static char *snapshot_level_identifier = NULL;
14989 static int snapshot_level_nr = -1;
14990
14991 static void SaveEngineSnapshotValues_RND()
14992 {
14993   static int belt_base_active_element[4] =
14994   {
14995     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14996     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14997     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14998     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14999   };
15000   int i, j;
15001
15002   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15003   {
15004     int element = EL_CUSTOM_START + i;
15005
15006     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15007   }
15008
15009   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15010   {
15011     int element = EL_GROUP_START + i;
15012
15013     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15014   }
15015
15016   for (i = 0; i < 4; i++)
15017   {
15018     for (j = 0; j < NUM_BELT_PARTS; j++)
15019     {
15020       int element = belt_base_active_element[i] + j;
15021       int graphic = el2img(element);
15022       int anim_mode = graphic_info[graphic].anim_mode;
15023
15024       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15025       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15026     }
15027   }
15028 }
15029
15030 static void LoadEngineSnapshotValues_RND()
15031 {
15032   unsigned int num_random_calls = game.num_random_calls;
15033   int i, j;
15034
15035   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15036   {
15037     int element = EL_CUSTOM_START + i;
15038
15039     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15040   }
15041
15042   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15043   {
15044     int element = EL_GROUP_START + i;
15045
15046     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15047   }
15048
15049   for (i = 0; i < 4; i++)
15050   {
15051     for (j = 0; j < NUM_BELT_PARTS; j++)
15052     {
15053       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15054       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15055
15056       graphic_info[graphic].anim_mode = anim_mode;
15057     }
15058   }
15059
15060   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15061   {
15062     InitRND(tape.random_seed);
15063     for (i = 0; i < num_random_calls; i++)
15064       RND(1);
15065   }
15066
15067   if (game.num_random_calls != num_random_calls)
15068   {
15069     Error(ERR_INFO, "number of random calls out of sync");
15070     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15071     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15072     Error(ERR_EXIT, "this should not happen -- please debug");
15073   }
15074 }
15075
15076 void FreeEngineSnapshotSingle()
15077 {
15078   FreeSnapshotSingle();
15079
15080   setString(&snapshot_level_identifier, NULL);
15081   snapshot_level_nr = -1;
15082 }
15083
15084 void FreeEngineSnapshotList()
15085 {
15086   FreeSnapshotList();
15087 }
15088
15089 ListNode *SaveEngineSnapshotBuffers()
15090 {
15091   ListNode *buffers = NULL;
15092
15093   /* copy some special values to a structure better suited for the snapshot */
15094
15095   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15096     SaveEngineSnapshotValues_RND();
15097   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15098     SaveEngineSnapshotValues_EM();
15099   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15100     SaveEngineSnapshotValues_SP(&buffers);
15101   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15102     SaveEngineSnapshotValues_MM(&buffers);
15103
15104   /* save values stored in special snapshot structure */
15105
15106   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15107     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15108   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15109     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15110   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15111     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15112   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15113     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15114
15115   /* save further RND engine values */
15116
15117   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15118   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15119   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15120
15121   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15122   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15123   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15124   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15125
15126   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15127   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15128   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15131
15132   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15135
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15137
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15139
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15142
15143   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15147   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15149   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15151   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15154   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15161
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15164
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15168
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15171
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15173   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15176   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15177
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15180
15181 #if 0
15182   ListNode *node = engine_snapshot_list_rnd;
15183   int num_bytes = 0;
15184
15185   while (node != NULL)
15186   {
15187     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15188
15189     node = node->next;
15190   }
15191
15192   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15193 #endif
15194
15195   return buffers;
15196 }
15197
15198 void SaveEngineSnapshotSingle()
15199 {
15200   ListNode *buffers = SaveEngineSnapshotBuffers();
15201
15202   /* finally save all snapshot buffers to single snapshot */
15203   SaveSnapshotSingle(buffers);
15204
15205   /* save level identification information */
15206   setString(&snapshot_level_identifier, leveldir_current->identifier);
15207   snapshot_level_nr = level_nr;
15208 }
15209
15210 boolean CheckSaveEngineSnapshotToList()
15211 {
15212   boolean save_snapshot =
15213     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15214      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15215       game.snapshot.changed_action) ||
15216      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15217       game.snapshot.collected_item));
15218
15219   game.snapshot.changed_action = FALSE;
15220   game.snapshot.collected_item = FALSE;
15221   game.snapshot.save_snapshot = save_snapshot;
15222
15223   return save_snapshot;
15224 }
15225
15226 void SaveEngineSnapshotToList()
15227 {
15228   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15229       tape.quick_resume)
15230     return;
15231
15232   ListNode *buffers = SaveEngineSnapshotBuffers();
15233
15234   /* finally save all snapshot buffers to snapshot list */
15235   SaveSnapshotToList(buffers);
15236 }
15237
15238 void SaveEngineSnapshotToListInitial()
15239 {
15240   FreeEngineSnapshotList();
15241
15242   SaveEngineSnapshotToList();
15243 }
15244
15245 void LoadEngineSnapshotValues()
15246 {
15247   /* restore special values from snapshot structure */
15248
15249   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15250     LoadEngineSnapshotValues_RND();
15251   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15252     LoadEngineSnapshotValues_EM();
15253   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15254     LoadEngineSnapshotValues_SP();
15255   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15256     LoadEngineSnapshotValues_MM();
15257 }
15258
15259 void LoadEngineSnapshotSingle()
15260 {
15261   LoadSnapshotSingle();
15262
15263   LoadEngineSnapshotValues();
15264 }
15265
15266 void LoadEngineSnapshot_Undo(int steps)
15267 {
15268   LoadSnapshotFromList_Older(steps);
15269
15270   LoadEngineSnapshotValues();
15271 }
15272
15273 void LoadEngineSnapshot_Redo(int steps)
15274 {
15275   LoadSnapshotFromList_Newer(steps);
15276
15277   LoadEngineSnapshotValues();
15278 }
15279
15280 boolean CheckEngineSnapshotSingle()
15281 {
15282   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15283           snapshot_level_nr == level_nr);
15284 }
15285
15286 boolean CheckEngineSnapshotList()
15287 {
15288   return CheckSnapshotList();
15289 }
15290
15291
15292 /* ---------- new game button stuff ---------------------------------------- */
15293
15294 static struct
15295 {
15296   int graphic;
15297   struct XY *pos;
15298   int gadget_id;
15299   boolean *setup_value;
15300   boolean allowed_on_tape;
15301   char *infotext;
15302 } gamebutton_info[NUM_GAME_BUTTONS] =
15303 {
15304   {
15305     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15306     GAME_CTRL_ID_STOP,                          NULL,
15307     TRUE,                                       "stop game"
15308   },
15309   {
15310     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15311     GAME_CTRL_ID_PAUSE,                         NULL,
15312     TRUE,                                       "pause game"
15313   },
15314   {
15315     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15316     GAME_CTRL_ID_PLAY,                          NULL,
15317     TRUE,                                       "play game"
15318   },
15319   {
15320     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15321     GAME_CTRL_ID_UNDO,                          NULL,
15322     TRUE,                                       "undo step"
15323   },
15324   {
15325     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15326     GAME_CTRL_ID_REDO,                          NULL,
15327     TRUE,                                       "redo step"
15328   },
15329   {
15330     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15331     GAME_CTRL_ID_SAVE,                          NULL,
15332     TRUE,                                       "save game"
15333   },
15334   {
15335     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15336     GAME_CTRL_ID_PAUSE2,                        NULL,
15337     TRUE,                                       "pause game"
15338   },
15339   {
15340     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15341     GAME_CTRL_ID_LOAD,                          NULL,
15342     TRUE,                                       "load game"
15343   },
15344   {
15345     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15346     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15347     FALSE,                                      "stop game"
15348   },
15349   {
15350     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15351     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15352     FALSE,                                      "pause game"
15353   },
15354   {
15355     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15356     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15357     FALSE,                                      "play game"
15358   },
15359   {
15360     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15361     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15362     TRUE,                                       "background music on/off"
15363   },
15364   {
15365     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15366     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15367     TRUE,                                       "sound loops on/off"
15368   },
15369   {
15370     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15371     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15372     TRUE,                                       "normal sounds on/off"
15373   },
15374   {
15375     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15376     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15377     FALSE,                                      "background music on/off"
15378   },
15379   {
15380     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15381     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15382     FALSE,                                      "sound loops on/off"
15383   },
15384   {
15385     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15386     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15387     FALSE,                                      "normal sounds on/off"
15388   }
15389 };
15390
15391 void CreateGameButtons()
15392 {
15393   int i;
15394
15395   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15396   {
15397     int graphic = gamebutton_info[i].graphic;
15398     struct GraphicInfo *gfx = &graphic_info[graphic];
15399     struct XY *pos = gamebutton_info[i].pos;
15400     struct GadgetInfo *gi;
15401     int button_type;
15402     boolean checked;
15403     unsigned int event_mask;
15404     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15405     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15406     int base_x = (on_tape ? VX : DX);
15407     int base_y = (on_tape ? VY : DY);
15408     int gd_x   = gfx->src_x;
15409     int gd_y   = gfx->src_y;
15410     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15411     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15412     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15413     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15414     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15415     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15416     int id = i;
15417
15418     if (gfx->bitmap == NULL)
15419     {
15420       game_gadget[id] = NULL;
15421
15422       continue;
15423     }
15424
15425     if (id == GAME_CTRL_ID_STOP ||
15426         id == GAME_CTRL_ID_PANEL_STOP ||
15427         id == GAME_CTRL_ID_PLAY ||
15428         id == GAME_CTRL_ID_PANEL_PLAY ||
15429         id == GAME_CTRL_ID_SAVE ||
15430         id == GAME_CTRL_ID_LOAD)
15431     {
15432       button_type = GD_TYPE_NORMAL_BUTTON;
15433       checked = FALSE;
15434       event_mask = GD_EVENT_RELEASED;
15435     }
15436     else if (id == GAME_CTRL_ID_UNDO ||
15437              id == GAME_CTRL_ID_REDO)
15438     {
15439       button_type = GD_TYPE_NORMAL_BUTTON;
15440       checked = FALSE;
15441       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15442     }
15443     else
15444     {
15445       button_type = GD_TYPE_CHECK_BUTTON;
15446       checked = (gamebutton_info[i].setup_value != NULL ?
15447                  *gamebutton_info[i].setup_value : FALSE);
15448       event_mask = GD_EVENT_PRESSED;
15449     }
15450
15451     gi = CreateGadget(GDI_CUSTOM_ID, id,
15452                       GDI_IMAGE_ID, graphic,
15453                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15454                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15455                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15456                       GDI_WIDTH, gfx->width,
15457                       GDI_HEIGHT, gfx->height,
15458                       GDI_TYPE, button_type,
15459                       GDI_STATE, GD_BUTTON_UNPRESSED,
15460                       GDI_CHECKED, checked,
15461                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15462                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15463                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15464                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15465                       GDI_DIRECT_DRAW, FALSE,
15466                       GDI_EVENT_MASK, event_mask,
15467                       GDI_CALLBACK_ACTION, HandleGameButtons,
15468                       GDI_END);
15469
15470     if (gi == NULL)
15471       Error(ERR_EXIT, "cannot create gadget");
15472
15473     game_gadget[id] = gi;
15474   }
15475 }
15476
15477 void FreeGameButtons()
15478 {
15479   int i;
15480
15481   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15482     FreeGadget(game_gadget[i]);
15483 }
15484
15485 static void UnmapGameButtonsAtSamePosition(int id)
15486 {
15487   int i;
15488
15489   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15490     if (i != id &&
15491         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15492         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15493       UnmapGadget(game_gadget[i]);
15494 }
15495
15496 static void UnmapGameButtonsAtSamePosition_All()
15497 {
15498   if (setup.show_snapshot_buttons)
15499   {
15500     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15501     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15502     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15503   }
15504   else
15505   {
15506     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15507     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15508     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15509
15510     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
15511     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
15512     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
15513   }
15514 }
15515
15516 static void MapGameButtonsAtSamePosition(int id)
15517 {
15518   int i;
15519
15520   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15521     if (i != id &&
15522         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15523         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15524       MapGadget(game_gadget[i]);
15525
15526   UnmapGameButtonsAtSamePosition_All();
15527 }
15528
15529 void MapUndoRedoButtons()
15530 {
15531   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15532   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15533
15534   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15535   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15536
15537   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15538 }
15539
15540 void UnmapUndoRedoButtons()
15541 {
15542   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15543   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15544
15545   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15546   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15547
15548   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15549 }
15550
15551 void MapGameButtonsExt(boolean on_tape)
15552 {
15553   int i;
15554
15555   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15556     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
15557         i != GAME_CTRL_ID_UNDO &&
15558         i != GAME_CTRL_ID_REDO)
15559       MapGadget(game_gadget[i]);
15560
15561   UnmapGameButtonsAtSamePosition_All();
15562
15563   RedrawGameButtons();
15564 }
15565
15566 void UnmapGameButtonsExt(boolean on_tape)
15567 {
15568   int i;
15569
15570   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15571     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15572       UnmapGadget(game_gadget[i]);
15573 }
15574
15575 void RedrawGameButtonsExt(boolean on_tape)
15576 {
15577   int i;
15578
15579   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15580     if (!on_tape || gamebutton_info[i].allowed_on_tape)
15581       RedrawGadget(game_gadget[i]);
15582
15583   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15584   redraw_mask &= ~REDRAW_ALL;
15585 }
15586
15587 void SetGadgetState(struct GadgetInfo *gi, boolean state)
15588 {
15589   if (gi == NULL)
15590     return;
15591
15592   gi->checked = state;
15593 }
15594
15595 void RedrawSoundButtonGadget(int id)
15596 {
15597   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
15598              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
15599              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
15600              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
15601              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
15602              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
15603              id);
15604
15605   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
15606   RedrawGadget(game_gadget[id2]);
15607 }
15608
15609 void MapGameButtons()
15610 {
15611   MapGameButtonsExt(FALSE);
15612 }
15613
15614 void UnmapGameButtons()
15615 {
15616   UnmapGameButtonsExt(FALSE);
15617 }
15618
15619 void RedrawGameButtons()
15620 {
15621   RedrawGameButtonsExt(FALSE);
15622 }
15623
15624 void MapGameButtonsOnTape()
15625 {
15626   MapGameButtonsExt(TRUE);
15627 }
15628
15629 void UnmapGameButtonsOnTape()
15630 {
15631   UnmapGameButtonsExt(TRUE);
15632 }
15633
15634 void RedrawGameButtonsOnTape()
15635 {
15636   RedrawGameButtonsExt(TRUE);
15637 }
15638
15639 void GameUndoRedoExt()
15640 {
15641   ClearPlayerAction();
15642
15643   tape.pausing = TRUE;
15644
15645   RedrawPlayfield();
15646   UpdateAndDisplayGameControlValues();
15647
15648   DrawCompleteVideoDisplay();
15649   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15650   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15651   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15652
15653   BackToFront();
15654 }
15655
15656 void GameUndo(int steps)
15657 {
15658   if (!CheckEngineSnapshotList())
15659     return;
15660
15661   LoadEngineSnapshot_Undo(steps);
15662
15663   GameUndoRedoExt();
15664 }
15665
15666 void GameRedo(int steps)
15667 {
15668   if (!CheckEngineSnapshotList())
15669     return;
15670
15671   LoadEngineSnapshot_Redo(steps);
15672
15673   GameUndoRedoExt();
15674 }
15675
15676 static void HandleGameButtonsExt(int id, int button)
15677 {
15678   static boolean game_undo_executed = FALSE;
15679   int steps = BUTTON_STEPSIZE(button);
15680   boolean handle_game_buttons =
15681     (game_status == GAME_MODE_PLAYING ||
15682      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15683
15684   if (!handle_game_buttons)
15685     return;
15686
15687   switch (id)
15688   {
15689     case GAME_CTRL_ID_STOP:
15690     case GAME_CTRL_ID_PANEL_STOP:
15691       if (game_status == GAME_MODE_MAIN)
15692         break;
15693
15694       if (tape.playing)
15695         TapeStop();
15696       else
15697         RequestQuitGame(TRUE);
15698
15699       break;
15700
15701     case GAME_CTRL_ID_PAUSE:
15702     case GAME_CTRL_ID_PAUSE2:
15703     case GAME_CTRL_ID_PANEL_PAUSE:
15704       if (network.enabled && game_status == GAME_MODE_PLAYING)
15705       {
15706         if (tape.pausing)
15707           SendToServer_ContinuePlaying();
15708         else
15709           SendToServer_PausePlaying();
15710       }
15711       else
15712         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15713
15714       game_undo_executed = FALSE;
15715
15716       break;
15717
15718     case GAME_CTRL_ID_PLAY:
15719     case GAME_CTRL_ID_PANEL_PLAY:
15720       if (game_status == GAME_MODE_MAIN)
15721       {
15722         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15723       }
15724       else if (tape.pausing)
15725       {
15726         if (network.enabled)
15727           SendToServer_ContinuePlaying();
15728         else
15729           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15730       }
15731       break;
15732
15733     case GAME_CTRL_ID_UNDO:
15734       // Important: When using "save snapshot when collecting an item" mode,
15735       // load last (current) snapshot for first "undo" after pressing "pause"
15736       // (else the last-but-one snapshot would be loaded, because the snapshot
15737       // pointer already points to the last snapshot when pressing "pause",
15738       // which is fine for "every step/move" mode, but not for "every collect")
15739       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15740           !game_undo_executed)
15741         steps--;
15742
15743       game_undo_executed = TRUE;
15744
15745       GameUndo(steps);
15746       break;
15747
15748     case GAME_CTRL_ID_REDO:
15749       GameRedo(steps);
15750       break;
15751
15752     case GAME_CTRL_ID_SAVE:
15753       TapeQuickSave();
15754       break;
15755
15756     case GAME_CTRL_ID_LOAD:
15757       TapeQuickLoad();
15758       break;
15759
15760     case SOUND_CTRL_ID_MUSIC:
15761     case SOUND_CTRL_ID_PANEL_MUSIC:
15762       if (setup.sound_music)
15763       { 
15764         setup.sound_music = FALSE;
15765
15766         FadeMusic();
15767       }
15768       else if (audio.music_available)
15769       { 
15770         setup.sound = setup.sound_music = TRUE;
15771
15772         SetAudioMode(setup.sound);
15773
15774         if (game_status == GAME_MODE_PLAYING)
15775           PlayLevelMusic();
15776       }
15777
15778       RedrawSoundButtonGadget(id);
15779
15780       break;
15781
15782     case SOUND_CTRL_ID_LOOPS:
15783     case SOUND_CTRL_ID_PANEL_LOOPS:
15784       if (setup.sound_loops)
15785         setup.sound_loops = FALSE;
15786       else if (audio.loops_available)
15787       {
15788         setup.sound = setup.sound_loops = TRUE;
15789
15790         SetAudioMode(setup.sound);
15791       }
15792
15793       RedrawSoundButtonGadget(id);
15794
15795       break;
15796
15797     case SOUND_CTRL_ID_SIMPLE:
15798     case SOUND_CTRL_ID_PANEL_SIMPLE:
15799       if (setup.sound_simple)
15800         setup.sound_simple = FALSE;
15801       else if (audio.sound_available)
15802       {
15803         setup.sound = setup.sound_simple = TRUE;
15804
15805         SetAudioMode(setup.sound);
15806       }
15807
15808       RedrawSoundButtonGadget(id);
15809
15810       break;
15811
15812     default:
15813       break;
15814   }
15815 }
15816
15817 static void HandleGameButtons(struct GadgetInfo *gi)
15818 {
15819   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15820 }
15821
15822 void HandleSoundButtonKeys(Key key)
15823 {
15824   if (key == setup.shortcut.sound_simple)
15825     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15826   else if (key == setup.shortcut.sound_loops)
15827     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15828   else if (key == setup.shortcut.sound_music)
15829     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15830 }