changed counting down time with the same step size (instead of 1 or 10)
[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 SOUND_CTRL_ID_MUSIC             8
1017 #define SOUND_CTRL_ID_LOOPS             9
1018 #define SOUND_CTRL_ID_SIMPLE            10
1019
1020 #define NUM_GAME_BUTTONS                11
1021
1022
1023 /* forward declaration for internal use */
1024
1025 static void CreateField(int, int, int);
1026
1027 static void ResetGfxAnimation(int, int);
1028
1029 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1030 static void AdvanceFrameAndPlayerCounters(int);
1031
1032 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1033 static boolean MovePlayer(struct PlayerInfo *, int, int);
1034 static void ScrollPlayer(struct PlayerInfo *, int);
1035 static void ScrollScreen(struct PlayerInfo *, int);
1036
1037 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1038 static boolean DigFieldByCE(int, int, int);
1039 static boolean SnapField(struct PlayerInfo *, int, int);
1040 static boolean DropElement(struct PlayerInfo *);
1041
1042 static void InitBeltMovement(void);
1043 static void CloseAllOpenTimegates(void);
1044 static void CheckGravityMovement(struct PlayerInfo *);
1045 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1046 static void KillPlayerUnlessEnemyProtected(int, int);
1047 static void KillPlayerUnlessExplosionProtected(int, int);
1048
1049 static void TestIfPlayerTouchesCustomElement(int, int);
1050 static void TestIfElementTouchesCustomElement(int, int);
1051 static void TestIfElementHitsCustomElement(int, int, int);
1052
1053 static void HandleElementChange(int, int, int);
1054 static void ExecuteCustomElementAction(int, int, int, int);
1055 static boolean ChangeElement(int, int, int, int);
1056
1057 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1058 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1059         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1060 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1061         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1062 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1063         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1064 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1065         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1066
1067 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1068 #define CheckElementChange(x, y, e, te, ev)                             \
1069         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1070 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1071         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1072 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1073         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1074
1075 static void PlayLevelSound(int, int, int);
1076 static void PlayLevelSoundNearest(int, int, int);
1077 static void PlayLevelSoundAction(int, int, int);
1078 static void PlayLevelSoundElementAction(int, int, int, int);
1079 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1080 static void PlayLevelSoundActionIfLoop(int, int, int);
1081 static void StopLevelSoundActionIfLoop(int, int, int);
1082 static void PlayLevelMusic();
1083 static void FadeLevelSoundsAndMusic();
1084
1085 static void HandleGameButtons(struct GadgetInfo *);
1086
1087 int AmoebeNachbarNr(int, int);
1088 void AmoebeUmwandeln(int, int);
1089 void ContinueMoving(int, int);
1090 void Bang(int, int);
1091 void InitMovDir(int, int);
1092 void InitAmoebaNr(int, int);
1093 int NewHiScore(void);
1094
1095 void TestIfGoodThingHitsBadThing(int, int, int);
1096 void TestIfBadThingHitsGoodThing(int, int, int);
1097 void TestIfPlayerTouchesBadThing(int, int);
1098 void TestIfPlayerRunsIntoBadThing(int, int, int);
1099 void TestIfBadThingTouchesPlayer(int, int);
1100 void TestIfBadThingRunsIntoPlayer(int, int, int);
1101 void TestIfFriendTouchesBadThing(int, int);
1102 void TestIfBadThingTouchesFriend(int, int);
1103 void TestIfBadThingTouchesOtherBadThing(int, int);
1104 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1105
1106 void KillPlayer(struct PlayerInfo *);
1107 void BuryPlayer(struct PlayerInfo *);
1108 void RemovePlayer(struct PlayerInfo *);
1109
1110 static int getInvisibleActiveFromInvisibleElement(int);
1111 static int getInvisibleFromInvisibleActiveElement(int);
1112
1113 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1114
1115 /* for detection of endless loops, caused by custom element programming */
1116 /* (using maximal playfield width x 10 is just a rough approximation) */
1117 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1118
1119 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1120 {                                                                       \
1121   if (recursion_loop_detected)                                          \
1122     return (rc);                                                        \
1123                                                                         \
1124   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1125   {                                                                     \
1126     recursion_loop_detected = TRUE;                                     \
1127     recursion_loop_element = (e);                                       \
1128   }                                                                     \
1129                                                                         \
1130   recursion_loop_depth++;                                               \
1131 }
1132
1133 #define RECURSION_LOOP_DETECTION_END()                                  \
1134 {                                                                       \
1135   recursion_loop_depth--;                                               \
1136 }
1137
1138 static int recursion_loop_depth;
1139 static boolean recursion_loop_detected;
1140 static boolean recursion_loop_element;
1141
1142 static int map_player_action[MAX_PLAYERS];
1143
1144
1145 /* ------------------------------------------------------------------------- */
1146 /* definition of elements that automatically change to other elements after  */
1147 /* a specified time, eventually calling a function when changing             */
1148 /* ------------------------------------------------------------------------- */
1149
1150 /* forward declaration for changer functions */
1151 static void InitBuggyBase(int, int);
1152 static void WarnBuggyBase(int, int);
1153
1154 static void InitTrap(int, int);
1155 static void ActivateTrap(int, int);
1156 static void ChangeActiveTrap(int, int);
1157
1158 static void InitRobotWheel(int, int);
1159 static void RunRobotWheel(int, int);
1160 static void StopRobotWheel(int, int);
1161
1162 static void InitTimegateWheel(int, int);
1163 static void RunTimegateWheel(int, int);
1164
1165 static void InitMagicBallDelay(int, int);
1166 static void ActivateMagicBall(int, int);
1167
1168 struct ChangingElementInfo
1169 {
1170   int element;
1171   int target_element;
1172   int change_delay;
1173   void (*pre_change_function)(int x, int y);
1174   void (*change_function)(int x, int y);
1175   void (*post_change_function)(int x, int y);
1176 };
1177
1178 static struct ChangingElementInfo change_delay_list[] =
1179 {
1180   {
1181     EL_NUT_BREAKING,
1182     EL_EMERALD,
1183     6,
1184     NULL,
1185     NULL,
1186     NULL
1187   },
1188   {
1189     EL_PEARL_BREAKING,
1190     EL_EMPTY,
1191     8,
1192     NULL,
1193     NULL,
1194     NULL
1195   },
1196   {
1197     EL_EXIT_OPENING,
1198     EL_EXIT_OPEN,
1199     29,
1200     NULL,
1201     NULL,
1202     NULL
1203   },
1204   {
1205     EL_EXIT_CLOSING,
1206     EL_EXIT_CLOSED,
1207     29,
1208     NULL,
1209     NULL,
1210     NULL
1211   },
1212   {
1213     EL_STEEL_EXIT_OPENING,
1214     EL_STEEL_EXIT_OPEN,
1215     29,
1216     NULL,
1217     NULL,
1218     NULL
1219   },
1220   {
1221     EL_STEEL_EXIT_CLOSING,
1222     EL_STEEL_EXIT_CLOSED,
1223     29,
1224     NULL,
1225     NULL,
1226     NULL
1227   },
1228   {
1229     EL_EM_EXIT_OPENING,
1230     EL_EM_EXIT_OPEN,
1231     29,
1232     NULL,
1233     NULL,
1234     NULL
1235   },
1236   {
1237     EL_EM_EXIT_CLOSING,
1238     EL_EMPTY,
1239     29,
1240     NULL,
1241     NULL,
1242     NULL
1243   },
1244   {
1245     EL_EM_STEEL_EXIT_OPENING,
1246     EL_EM_STEEL_EXIT_OPEN,
1247     29,
1248     NULL,
1249     NULL,
1250     NULL
1251   },
1252   {
1253     EL_EM_STEEL_EXIT_CLOSING,
1254     EL_STEELWALL,
1255     29,
1256     NULL,
1257     NULL,
1258     NULL
1259   },
1260   {
1261     EL_SP_EXIT_OPENING,
1262     EL_SP_EXIT_OPEN,
1263     29,
1264     NULL,
1265     NULL,
1266     NULL
1267   },
1268   {
1269     EL_SP_EXIT_CLOSING,
1270     EL_SP_EXIT_CLOSED,
1271     29,
1272     NULL,
1273     NULL,
1274     NULL
1275   },
1276   {
1277     EL_SWITCHGATE_OPENING,
1278     EL_SWITCHGATE_OPEN,
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_SWITCHGATE_CLOSING,
1286     EL_SWITCHGATE_CLOSED,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_TIMEGATE_OPENING,
1294     EL_TIMEGATE_OPEN,
1295     29,
1296     NULL,
1297     NULL,
1298     NULL
1299   },
1300   {
1301     EL_TIMEGATE_CLOSING,
1302     EL_TIMEGATE_CLOSED,
1303     29,
1304     NULL,
1305     NULL,
1306     NULL
1307   },
1308
1309   {
1310     EL_ACID_SPLASH_LEFT,
1311     EL_EMPTY,
1312     8,
1313     NULL,
1314     NULL,
1315     NULL
1316   },
1317   {
1318     EL_ACID_SPLASH_RIGHT,
1319     EL_EMPTY,
1320     8,
1321     NULL,
1322     NULL,
1323     NULL
1324   },
1325   {
1326     EL_SP_BUGGY_BASE,
1327     EL_SP_BUGGY_BASE_ACTIVATING,
1328     0,
1329     InitBuggyBase,
1330     NULL,
1331     NULL
1332   },
1333   {
1334     EL_SP_BUGGY_BASE_ACTIVATING,
1335     EL_SP_BUGGY_BASE_ACTIVE,
1336     0,
1337     InitBuggyBase,
1338     NULL,
1339     NULL
1340   },
1341   {
1342     EL_SP_BUGGY_BASE_ACTIVE,
1343     EL_SP_BUGGY_BASE,
1344     0,
1345     InitBuggyBase,
1346     WarnBuggyBase,
1347     NULL
1348   },
1349   {
1350     EL_TRAP,
1351     EL_TRAP_ACTIVE,
1352     0,
1353     InitTrap,
1354     NULL,
1355     ActivateTrap
1356   },
1357   {
1358     EL_TRAP_ACTIVE,
1359     EL_TRAP,
1360     31,
1361     NULL,
1362     ChangeActiveTrap,
1363     NULL
1364   },
1365   {
1366     EL_ROBOT_WHEEL_ACTIVE,
1367     EL_ROBOT_WHEEL,
1368     0,
1369     InitRobotWheel,
1370     RunRobotWheel,
1371     StopRobotWheel
1372   },
1373   {
1374     EL_TIMEGATE_SWITCH_ACTIVE,
1375     EL_TIMEGATE_SWITCH,
1376     0,
1377     InitTimegateWheel,
1378     RunTimegateWheel,
1379     NULL
1380   },
1381   {
1382     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1383     EL_DC_TIMEGATE_SWITCH,
1384     0,
1385     InitTimegateWheel,
1386     RunTimegateWheel,
1387     NULL
1388   },
1389   {
1390     EL_EMC_MAGIC_BALL_ACTIVE,
1391     EL_EMC_MAGIC_BALL_ACTIVE,
1392     0,
1393     InitMagicBallDelay,
1394     NULL,
1395     ActivateMagicBall
1396   },
1397   {
1398     EL_EMC_SPRING_BUMPER_ACTIVE,
1399     EL_EMC_SPRING_BUMPER,
1400     8,
1401     NULL,
1402     NULL,
1403     NULL
1404   },
1405   {
1406     EL_DIAGONAL_SHRINKING,
1407     EL_UNDEFINED,
1408     0,
1409     NULL,
1410     NULL,
1411     NULL
1412   },
1413   {
1414     EL_DIAGONAL_GROWING,
1415     EL_UNDEFINED,
1416     0,
1417     NULL,
1418     NULL,
1419     NULL,
1420   },
1421
1422   {
1423     EL_UNDEFINED,
1424     EL_UNDEFINED,
1425     -1,
1426     NULL,
1427     NULL,
1428     NULL
1429   }
1430 };
1431
1432 struct
1433 {
1434   int element;
1435   int push_delay_fixed, push_delay_random;
1436 }
1437 push_delay_list[] =
1438 {
1439   { EL_SPRING,                  0, 0 },
1440   { EL_BALLOON,                 0, 0 },
1441
1442   { EL_SOKOBAN_OBJECT,          2, 0 },
1443   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1444   { EL_SATELLITE,               2, 0 },
1445   { EL_SP_DISK_YELLOW,          2, 0 },
1446
1447   { EL_UNDEFINED,               0, 0 },
1448 };
1449
1450 struct
1451 {
1452   int element;
1453   int move_stepsize;
1454 }
1455 move_stepsize_list[] =
1456 {
1457   { EL_AMOEBA_DROP,             2 },
1458   { EL_AMOEBA_DROPPING,         2 },
1459   { EL_QUICKSAND_FILLING,       1 },
1460   { EL_QUICKSAND_EMPTYING,      1 },
1461   { EL_QUICKSAND_FAST_FILLING,  2 },
1462   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1463   { EL_MAGIC_WALL_FILLING,      2 },
1464   { EL_MAGIC_WALL_EMPTYING,     2 },
1465   { EL_BD_MAGIC_WALL_FILLING,   2 },
1466   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1467   { EL_DC_MAGIC_WALL_FILLING,   2 },
1468   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1469
1470   { EL_UNDEFINED,               0 },
1471 };
1472
1473 struct
1474 {
1475   int element;
1476   int count;
1477 }
1478 collect_count_list[] =
1479 {
1480   { EL_EMERALD,                 1 },
1481   { EL_BD_DIAMOND,              1 },
1482   { EL_EMERALD_YELLOW,          1 },
1483   { EL_EMERALD_RED,             1 },
1484   { EL_EMERALD_PURPLE,          1 },
1485   { EL_DIAMOND,                 3 },
1486   { EL_SP_INFOTRON,             1 },
1487   { EL_PEARL,                   5 },
1488   { EL_CRYSTAL,                 8 },
1489
1490   { EL_UNDEFINED,               0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int direction;
1497 }
1498 access_direction_list[] =
1499 {
1500   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1501   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1502   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1503   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1504   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1505   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1506   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1507   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1508   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1509   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1510   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1511
1512   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1513   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1514   { EL_SP_PORT_UP,                                                   MV_DOWN },
1515   { EL_SP_PORT_DOWN,                                         MV_UP           },
1516   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1517   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1518   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1519   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1520   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1521   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1522   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1523   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1524   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1525   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1526   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1527   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1528   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1529   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1530   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1531
1532   { EL_UNDEFINED,                       MV_NONE                              }
1533 };
1534
1535 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1536
1537 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1538 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1539 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1540                                  IS_JUST_CHANGING(x, y))
1541
1542 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1543
1544 /* static variables for playfield scan mode (scanning forward or backward) */
1545 static int playfield_scan_start_x = 0;
1546 static int playfield_scan_start_y = 0;
1547 static int playfield_scan_delta_x = 1;
1548 static int playfield_scan_delta_y = 1;
1549
1550 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1551                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1552                                      (y) += playfield_scan_delta_y)     \
1553                                 for ((x) = playfield_scan_start_x;      \
1554                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1555                                      (x) += playfield_scan_delta_x)
1556
1557 #ifdef DEBUG
1558 void DEBUG_SetMaximumDynamite()
1559 {
1560   int i;
1561
1562   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1563     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1564       local_player->inventory_element[local_player->inventory_size++] =
1565         EL_DYNAMITE;
1566 }
1567 #endif
1568
1569 static void InitPlayfieldScanModeVars()
1570 {
1571   if (game.use_reverse_scan_direction)
1572   {
1573     playfield_scan_start_x = lev_fieldx - 1;
1574     playfield_scan_start_y = lev_fieldy - 1;
1575
1576     playfield_scan_delta_x = -1;
1577     playfield_scan_delta_y = -1;
1578   }
1579   else
1580   {
1581     playfield_scan_start_x = 0;
1582     playfield_scan_start_y = 0;
1583
1584     playfield_scan_delta_x = 1;
1585     playfield_scan_delta_y = 1;
1586   }
1587 }
1588
1589 static void InitPlayfieldScanMode(int mode)
1590 {
1591   game.use_reverse_scan_direction =
1592     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1593
1594   InitPlayfieldScanModeVars();
1595 }
1596
1597 static int get_move_delay_from_stepsize(int move_stepsize)
1598 {
1599   move_stepsize =
1600     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1601
1602   /* make sure that stepsize value is always a power of 2 */
1603   move_stepsize = (1 << log_2(move_stepsize));
1604
1605   return TILEX / move_stepsize;
1606 }
1607
1608 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1609                                boolean init_game)
1610 {
1611   int player_nr = player->index_nr;
1612   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1613   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1614
1615   /* do no immediately change move delay -- the player might just be moving */
1616   player->move_delay_value_next = move_delay;
1617
1618   /* information if player can move must be set separately */
1619   player->cannot_move = cannot_move;
1620
1621   if (init_game)
1622   {
1623     player->move_delay       = game.initial_move_delay[player_nr];
1624     player->move_delay_value = game.initial_move_delay_value[player_nr];
1625
1626     player->move_delay_value_next = -1;
1627
1628     player->move_delay_reset_counter = 0;
1629   }
1630 }
1631
1632 void GetPlayerConfig()
1633 {
1634   GameFrameDelay = setup.game_frame_delay;
1635
1636   if (!audio.sound_available)
1637     setup.sound_simple = FALSE;
1638
1639   if (!audio.loops_available)
1640     setup.sound_loops = FALSE;
1641
1642   if (!audio.music_available)
1643     setup.sound_music = FALSE;
1644
1645   if (!video.fullscreen_available)
1646     setup.fullscreen = FALSE;
1647
1648   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1649
1650   SetAudioMode(setup.sound);
1651 }
1652
1653 int GetElementFromGroupElement(int element)
1654 {
1655   if (IS_GROUP_ELEMENT(element))
1656   {
1657     struct ElementGroupInfo *group = element_info[element].group;
1658     int last_anim_random_frame = gfx.anim_random_frame;
1659     int element_pos;
1660
1661     if (group->choice_mode == ANIM_RANDOM)
1662       gfx.anim_random_frame = RND(group->num_elements_resolved);
1663
1664     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1665                                     group->choice_mode, 0,
1666                                     group->choice_pos);
1667
1668     if (group->choice_mode == ANIM_RANDOM)
1669       gfx.anim_random_frame = last_anim_random_frame;
1670
1671     group->choice_pos++;
1672
1673     element = group->element_resolved[element_pos];
1674   }
1675
1676   return element;
1677 }
1678
1679 static void InitPlayerField(int x, int y, int element, boolean init_game)
1680 {
1681   if (element == EL_SP_MURPHY)
1682   {
1683     if (init_game)
1684     {
1685       if (stored_player[0].present)
1686       {
1687         Feld[x][y] = EL_SP_MURPHY_CLONE;
1688
1689         return;
1690       }
1691       else
1692       {
1693         stored_player[0].initial_element = element;
1694         stored_player[0].use_murphy = TRUE;
1695
1696         if (!level.use_artwork_element[0])
1697           stored_player[0].artwork_element = EL_SP_MURPHY;
1698       }
1699
1700       Feld[x][y] = EL_PLAYER_1;
1701     }
1702   }
1703
1704   if (init_game)
1705   {
1706     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1707     int jx = player->jx, jy = player->jy;
1708
1709     player->present = TRUE;
1710
1711     player->block_last_field = (element == EL_SP_MURPHY ?
1712                                 level.sp_block_last_field :
1713                                 level.block_last_field);
1714
1715     /* ---------- initialize player's last field block delay --------------- */
1716
1717     /* always start with reliable default value (no adjustment needed) */
1718     player->block_delay_adjustment = 0;
1719
1720     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1721     if (player->block_last_field && element == EL_SP_MURPHY)
1722       player->block_delay_adjustment = 1;
1723
1724     /* special case 2: in game engines before 3.1.1, blocking was different */
1725     if (game.use_block_last_field_bug)
1726       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1727
1728     if (!options.network || player->connected)
1729     {
1730       player->active = TRUE;
1731
1732       /* remove potentially duplicate players */
1733       if (StorePlayer[jx][jy] == Feld[x][y])
1734         StorePlayer[jx][jy] = 0;
1735
1736       StorePlayer[x][y] = Feld[x][y];
1737
1738 #if DEBUG_INIT_PLAYER
1739       if (options.debug)
1740       {
1741         printf("- player element %d activated", player->element_nr);
1742         printf(" (local player is %d and currently %s)\n",
1743                local_player->element_nr,
1744                local_player->active ? "active" : "not active");
1745       }
1746     }
1747 #endif
1748
1749     Feld[x][y] = EL_EMPTY;
1750
1751     player->jx = player->last_jx = x;
1752     player->jy = player->last_jy = y;
1753   }
1754
1755   if (!init_game)
1756   {
1757     int player_nr = GET_PLAYER_NR(element);
1758     struct PlayerInfo *player = &stored_player[player_nr];
1759
1760     if (player->active && player->killed)
1761       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1762   }
1763 }
1764
1765 static void InitField(int x, int y, boolean init_game)
1766 {
1767   int element = Feld[x][y];
1768
1769   switch (element)
1770   {
1771     case EL_SP_MURPHY:
1772     case EL_PLAYER_1:
1773     case EL_PLAYER_2:
1774     case EL_PLAYER_3:
1775     case EL_PLAYER_4:
1776       InitPlayerField(x, y, element, init_game);
1777       break;
1778
1779     case EL_SOKOBAN_FIELD_PLAYER:
1780       element = Feld[x][y] = EL_PLAYER_1;
1781       InitField(x, y, init_game);
1782
1783       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1784       InitField(x, y, init_game);
1785       break;
1786
1787     case EL_SOKOBAN_FIELD_EMPTY:
1788       local_player->sokobanfields_still_needed++;
1789       break;
1790
1791     case EL_STONEBLOCK:
1792       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1793         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1794       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1795         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1796       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1797         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1798       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1799         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1800       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1801         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1802       break;
1803
1804     case EL_BUG:
1805     case EL_BUG_RIGHT:
1806     case EL_BUG_UP:
1807     case EL_BUG_LEFT:
1808     case EL_BUG_DOWN:
1809     case EL_SPACESHIP:
1810     case EL_SPACESHIP_RIGHT:
1811     case EL_SPACESHIP_UP:
1812     case EL_SPACESHIP_LEFT:
1813     case EL_SPACESHIP_DOWN:
1814     case EL_BD_BUTTERFLY:
1815     case EL_BD_BUTTERFLY_RIGHT:
1816     case EL_BD_BUTTERFLY_UP:
1817     case EL_BD_BUTTERFLY_LEFT:
1818     case EL_BD_BUTTERFLY_DOWN:
1819     case EL_BD_FIREFLY:
1820     case EL_BD_FIREFLY_RIGHT:
1821     case EL_BD_FIREFLY_UP:
1822     case EL_BD_FIREFLY_LEFT:
1823     case EL_BD_FIREFLY_DOWN:
1824     case EL_PACMAN_RIGHT:
1825     case EL_PACMAN_UP:
1826     case EL_PACMAN_LEFT:
1827     case EL_PACMAN_DOWN:
1828     case EL_YAMYAM:
1829     case EL_YAMYAM_LEFT:
1830     case EL_YAMYAM_RIGHT:
1831     case EL_YAMYAM_UP:
1832     case EL_YAMYAM_DOWN:
1833     case EL_DARK_YAMYAM:
1834     case EL_ROBOT:
1835     case EL_PACMAN:
1836     case EL_SP_SNIKSNAK:
1837     case EL_SP_ELECTRON:
1838     case EL_MOLE:
1839     case EL_MOLE_LEFT:
1840     case EL_MOLE_RIGHT:
1841     case EL_MOLE_UP:
1842     case EL_MOLE_DOWN:
1843       InitMovDir(x, y);
1844       break;
1845
1846     case EL_AMOEBA_FULL:
1847     case EL_BD_AMOEBA:
1848       InitAmoebaNr(x, y);
1849       break;
1850
1851     case EL_AMOEBA_DROP:
1852       if (y == lev_fieldy - 1)
1853       {
1854         Feld[x][y] = EL_AMOEBA_GROWING;
1855         Store[x][y] = EL_AMOEBA_WET;
1856       }
1857       break;
1858
1859     case EL_DYNAMITE_ACTIVE:
1860     case EL_SP_DISK_RED_ACTIVE:
1861     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1862     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1863     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1864     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1865       MovDelay[x][y] = 96;
1866       break;
1867
1868     case EL_EM_DYNAMITE_ACTIVE:
1869       MovDelay[x][y] = 32;
1870       break;
1871
1872     case EL_LAMP:
1873       local_player->lights_still_needed++;
1874       break;
1875
1876     case EL_PENGUIN:
1877       local_player->friends_still_needed++;
1878       break;
1879
1880     case EL_PIG:
1881     case EL_DRAGON:
1882       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1883       break;
1884
1885     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1886     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1887     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1888     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1889     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1890     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1891     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1892     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1893     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1894     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1895     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1896     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1897       if (init_game)
1898       {
1899         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1900         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1901         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1902
1903         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1904         {
1905           game.belt_dir[belt_nr] = belt_dir;
1906           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1907         }
1908         else    /* more than one switch -- set it like the first switch */
1909         {
1910           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1911         }
1912       }
1913       break;
1914
1915     case EL_LIGHT_SWITCH_ACTIVE:
1916       if (init_game)
1917         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1918       break;
1919
1920     case EL_INVISIBLE_STEELWALL:
1921     case EL_INVISIBLE_WALL:
1922     case EL_INVISIBLE_SAND:
1923       if (game.light_time_left > 0 ||
1924           game.lenses_time_left > 0)
1925         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1926       break;
1927
1928     case EL_EMC_MAGIC_BALL:
1929       if (game.ball_state)
1930         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1931       break;
1932
1933     case EL_EMC_MAGIC_BALL_SWITCH:
1934       if (game.ball_state)
1935         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1936       break;
1937
1938     case EL_TRIGGER_PLAYER:
1939     case EL_TRIGGER_ELEMENT:
1940     case EL_TRIGGER_CE_VALUE:
1941     case EL_TRIGGER_CE_SCORE:
1942     case EL_SELF:
1943     case EL_ANY_ELEMENT:
1944     case EL_CURRENT_CE_VALUE:
1945     case EL_CURRENT_CE_SCORE:
1946     case EL_PREV_CE_1:
1947     case EL_PREV_CE_2:
1948     case EL_PREV_CE_3:
1949     case EL_PREV_CE_4:
1950     case EL_PREV_CE_5:
1951     case EL_PREV_CE_6:
1952     case EL_PREV_CE_7:
1953     case EL_PREV_CE_8:
1954     case EL_NEXT_CE_1:
1955     case EL_NEXT_CE_2:
1956     case EL_NEXT_CE_3:
1957     case EL_NEXT_CE_4:
1958     case EL_NEXT_CE_5:
1959     case EL_NEXT_CE_6:
1960     case EL_NEXT_CE_7:
1961     case EL_NEXT_CE_8:
1962       /* reference elements should not be used on the playfield */
1963       Feld[x][y] = EL_EMPTY;
1964       break;
1965
1966     default:
1967       if (IS_CUSTOM_ELEMENT(element))
1968       {
1969         if (CAN_MOVE(element))
1970           InitMovDir(x, y);
1971
1972         if (!element_info[element].use_last_ce_value || init_game)
1973           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1974       }
1975       else if (IS_GROUP_ELEMENT(element))
1976       {
1977         Feld[x][y] = GetElementFromGroupElement(element);
1978
1979         InitField(x, y, init_game);
1980       }
1981
1982       break;
1983   }
1984
1985   if (!init_game)
1986     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1987 }
1988
1989 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1990 {
1991   InitField(x, y, init_game);
1992
1993   /* not needed to call InitMovDir() -- already done by InitField()! */
1994   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1995       CAN_MOVE(Feld[x][y]))
1996     InitMovDir(x, y);
1997 }
1998
1999 inline static void InitField_WithBug2(int x, int y, boolean init_game)
2000 {
2001   int old_element = Feld[x][y];
2002
2003   InitField(x, y, init_game);
2004
2005   /* not needed to call InitMovDir() -- already done by InitField()! */
2006   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2007       CAN_MOVE(old_element) &&
2008       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2009     InitMovDir(x, y);
2010
2011   /* this case is in fact a combination of not less than three bugs:
2012      first, it calls InitMovDir() for elements that can move, although this is
2013      already done by InitField(); then, it checks the element that was at this
2014      field _before_ the call to InitField() (which can change it); lastly, it
2015      was not called for "mole with direction" elements, which were treated as
2016      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2017   */
2018 }
2019
2020 static int get_key_element_from_nr(int key_nr)
2021 {
2022   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2023                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2024                           EL_EM_KEY_1 : EL_KEY_1);
2025
2026   return key_base_element + key_nr;
2027 }
2028
2029 static int get_next_dropped_element(struct PlayerInfo *player)
2030 {
2031   return (player->inventory_size > 0 ?
2032           player->inventory_element[player->inventory_size - 1] :
2033           player->inventory_infinite_element != EL_UNDEFINED ?
2034           player->inventory_infinite_element :
2035           player->dynabombs_left > 0 ?
2036           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2037           EL_UNDEFINED);
2038 }
2039
2040 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2041 {
2042   /* pos >= 0: get element from bottom of the stack;
2043      pos <  0: get element from top of the stack */
2044
2045   if (pos < 0)
2046   {
2047     int min_inventory_size = -pos;
2048     int inventory_pos = player->inventory_size - min_inventory_size;
2049     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2050
2051     return (player->inventory_size >= min_inventory_size ?
2052             player->inventory_element[inventory_pos] :
2053             player->inventory_infinite_element != EL_UNDEFINED ?
2054             player->inventory_infinite_element :
2055             player->dynabombs_left >= min_dynabombs_left ?
2056             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2057             EL_UNDEFINED);
2058   }
2059   else
2060   {
2061     int min_dynabombs_left = pos + 1;
2062     int min_inventory_size = pos + 1 - player->dynabombs_left;
2063     int inventory_pos = pos - player->dynabombs_left;
2064
2065     return (player->inventory_infinite_element != EL_UNDEFINED ?
2066             player->inventory_infinite_element :
2067             player->dynabombs_left >= min_dynabombs_left ?
2068             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2069             player->inventory_size >= min_inventory_size ?
2070             player->inventory_element[inventory_pos] :
2071             EL_UNDEFINED);
2072   }
2073 }
2074
2075 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2076 {
2077   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2078   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2079   int compare_result;
2080
2081   if (gpo1->sort_priority != gpo2->sort_priority)
2082     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2083   else
2084     compare_result = gpo1->nr - gpo2->nr;
2085
2086   return compare_result;
2087 }
2088
2089 int getPlayerInventorySize(int player_nr)
2090 {
2091   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2092     return level.native_em_level->ply[player_nr]->dynamite;
2093   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2094     return level.native_sp_level->game_sp->red_disk_count;
2095   else
2096     return stored_player[player_nr].inventory_size;
2097 }
2098
2099 void InitGameControlValues()
2100 {
2101   int i;
2102
2103   for (i = 0; game_panel_controls[i].nr != -1; i++)
2104   {
2105     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2106     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2107     struct TextPosInfo *pos = gpc->pos;
2108     int nr = gpc->nr;
2109     int type = gpc->type;
2110
2111     if (nr != i)
2112     {
2113       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2114       Error(ERR_EXIT, "this should not happen -- please debug");
2115     }
2116
2117     /* force update of game controls after initialization */
2118     gpc->value = gpc->last_value = -1;
2119     gpc->frame = gpc->last_frame = -1;
2120     gpc->gfx_frame = -1;
2121
2122     /* determine panel value width for later calculation of alignment */
2123     if (type == TYPE_INTEGER || type == TYPE_STRING)
2124     {
2125       pos->width = pos->size * getFontWidth(pos->font);
2126       pos->height = getFontHeight(pos->font);
2127     }
2128     else if (type == TYPE_ELEMENT)
2129     {
2130       pos->width = pos->size;
2131       pos->height = pos->size;
2132     }
2133
2134     /* fill structure for game panel draw order */
2135     gpo->nr = gpc->nr;
2136     gpo->sort_priority = pos->sort_priority;
2137   }
2138
2139   /* sort game panel controls according to sort_priority and control number */
2140   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2141         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2142 }
2143
2144 void UpdatePlayfieldElementCount()
2145 {
2146   boolean use_element_count = FALSE;
2147   int i, j, x, y;
2148
2149   /* first check if it is needed at all to calculate playfield element count */
2150   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2151     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2152       use_element_count = TRUE;
2153
2154   if (!use_element_count)
2155     return;
2156
2157   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2158     element_info[i].element_count = 0;
2159
2160   SCAN_PLAYFIELD(x, y)
2161   {
2162     element_info[Feld[x][y]].element_count++;
2163   }
2164
2165   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2166     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2167       if (IS_IN_GROUP(j, i))
2168         element_info[EL_GROUP_START + i].element_count +=
2169           element_info[j].element_count;
2170 }
2171
2172 void UpdateGameControlValues()
2173 {
2174   int i, k;
2175   int time = (local_player->LevelSolved ?
2176               local_player->LevelSolved_CountingTime :
2177               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2178               level.native_em_level->lev->time :
2179               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2180               level.native_sp_level->game_sp->time_played :
2181               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2182               game_mm.energy_left :
2183               game.no_time_limit ? TimePlayed : TimeLeft);
2184   int score = (local_player->LevelSolved ?
2185                local_player->LevelSolved_CountingScore :
2186                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187                level.native_em_level->lev->score :
2188                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189                level.native_sp_level->game_sp->score :
2190                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2191                game_mm.score :
2192                local_player->score);
2193   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194               level.native_em_level->lev->required :
2195               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196               level.native_sp_level->game_sp->infotrons_still_needed :
2197               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2198               game_mm.kettles_still_needed :
2199               local_player->gems_still_needed);
2200   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2201                      level.native_em_level->lev->required > 0 :
2202                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2203                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2204                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2205                      game_mm.kettles_still_needed > 0 ||
2206                      game_mm.lights_still_needed > 0 :
2207                      local_player->gems_still_needed > 0 ||
2208                      local_player->sokobanfields_still_needed > 0 ||
2209                      local_player->lights_still_needed > 0);
2210   int health = (local_player->LevelSolved ?
2211                 local_player->LevelSolved_CountingHealth :
2212                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2213                 MM_HEALTH(game_mm.laser_overload_value) :
2214                 local_player->health);
2215
2216   UpdatePlayfieldElementCount();
2217
2218   /* update game panel control values */
2219
2220   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2221   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2222
2223   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2224   for (i = 0; i < MAX_NUM_KEYS; i++)
2225     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2226   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2227   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2228
2229   if (game.centered_player_nr == -1)
2230   {
2231     for (i = 0; i < MAX_PLAYERS; i++)
2232     {
2233       /* only one player in Supaplex game engine */
2234       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2235         break;
2236
2237       for (k = 0; k < MAX_NUM_KEYS; k++)
2238       {
2239         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2240         {
2241           if (level.native_em_level->ply[i]->keys & (1 << k))
2242             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2243               get_key_element_from_nr(k);
2244         }
2245         else if (stored_player[i].key[k])
2246           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2247             get_key_element_from_nr(k);
2248       }
2249
2250       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2251         getPlayerInventorySize(i);
2252
2253       if (stored_player[i].num_white_keys > 0)
2254         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2255           EL_DC_KEY_WHITE;
2256
2257       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2258         stored_player[i].num_white_keys;
2259     }
2260   }
2261   else
2262   {
2263     int player_nr = game.centered_player_nr;
2264
2265     for (k = 0; k < MAX_NUM_KEYS; k++)
2266     {
2267       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2268       {
2269         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2270           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2271             get_key_element_from_nr(k);
2272       }
2273       else if (stored_player[player_nr].key[k])
2274         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2275           get_key_element_from_nr(k);
2276     }
2277
2278     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2279       getPlayerInventorySize(player_nr);
2280
2281     if (stored_player[player_nr].num_white_keys > 0)
2282       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2283
2284     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2285       stored_player[player_nr].num_white_keys;
2286   }
2287
2288   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2289   {
2290     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2291       get_inventory_element_from_pos(local_player, i);
2292     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2293       get_inventory_element_from_pos(local_player, -i - 1);
2294   }
2295
2296   game_panel_controls[GAME_PANEL_SCORE].value = score;
2297   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2298
2299   game_panel_controls[GAME_PANEL_TIME].value = time;
2300
2301   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2302   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2303   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2304
2305   if (game.no_time_limit)
2306     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2307   else
2308     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2309
2310   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2311   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2312
2313   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2314
2315   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2316     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2317      EL_EMPTY);
2318   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2319     local_player->shield_normal_time_left;
2320   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2321     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2322      EL_EMPTY);
2323   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2324     local_player->shield_deadly_time_left;
2325
2326   game_panel_controls[GAME_PANEL_EXIT].value =
2327     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2328
2329   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2330     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2331   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2332     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2333      EL_EMC_MAGIC_BALL_SWITCH);
2334
2335   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2336     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2337   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2338     game.light_time_left;
2339
2340   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2341     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2342   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2343     game.timegate_time_left;
2344
2345   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2346     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2347
2348   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2349     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2350   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2351     game.lenses_time_left;
2352
2353   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2354     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2355   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2356     game.magnify_time_left;
2357
2358   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2359     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2360      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2361      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2362      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2363      EL_BALLOON_SWITCH_NONE);
2364
2365   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2366     local_player->dynabomb_count;
2367   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2368     local_player->dynabomb_size;
2369   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2370     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2371
2372   game_panel_controls[GAME_PANEL_PENGUINS].value =
2373     local_player->friends_still_needed;
2374
2375   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2376     local_player->sokobanfields_still_needed;
2377   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2378     local_player->sokobanfields_still_needed;
2379
2380   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2381     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2382
2383   for (i = 0; i < NUM_BELTS; i++)
2384   {
2385     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2386       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2387        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2388     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2389       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2390   }
2391
2392   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2393     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2394   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2395     game.magic_wall_time_left;
2396
2397   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2398     local_player->gravity;
2399
2400   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2401     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2402
2403   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2404     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2405       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2406        game.panel.element[i].id : EL_UNDEFINED);
2407
2408   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2410       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2411        element_info[game.panel.element_count[i].id].element_count : 0);
2412
2413   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2414     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2415       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2416        element_info[game.panel.ce_score[i].id].collect_score : 0);
2417
2418   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2420       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2421        element_info[game.panel.ce_score_element[i].id].collect_score :
2422        EL_UNDEFINED);
2423
2424   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2425   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2426   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2427
2428   /* update game panel control frames */
2429
2430   for (i = 0; game_panel_controls[i].nr != -1; i++)
2431   {
2432     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2433
2434     if (gpc->type == TYPE_ELEMENT)
2435     {
2436       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2437       {
2438         int last_anim_random_frame = gfx.anim_random_frame;
2439         int element = gpc->value;
2440         int graphic = el2panelimg(element);
2441
2442         if (gpc->value != gpc->last_value)
2443         {
2444           gpc->gfx_frame = 0;
2445           gpc->gfx_random = INIT_GFX_RANDOM();
2446         }
2447         else
2448         {
2449           gpc->gfx_frame++;
2450
2451           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2452               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2453             gpc->gfx_random = INIT_GFX_RANDOM();
2454         }
2455
2456         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2457           gfx.anim_random_frame = gpc->gfx_random;
2458
2459         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2460           gpc->gfx_frame = element_info[element].collect_score;
2461
2462         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2463                                               gpc->gfx_frame);
2464
2465         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2466           gfx.anim_random_frame = last_anim_random_frame;
2467       }
2468     }
2469     else if (gpc->type == TYPE_GRAPHIC)
2470     {
2471       if (gpc->graphic != IMG_UNDEFINED)
2472       {
2473         int last_anim_random_frame = gfx.anim_random_frame;
2474         int graphic = gpc->graphic;
2475
2476         if (gpc->value != gpc->last_value)
2477         {
2478           gpc->gfx_frame = 0;
2479           gpc->gfx_random = INIT_GFX_RANDOM();
2480         }
2481         else
2482         {
2483           gpc->gfx_frame++;
2484
2485           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2486               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2487             gpc->gfx_random = INIT_GFX_RANDOM();
2488         }
2489
2490         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2491           gfx.anim_random_frame = gpc->gfx_random;
2492
2493         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2494
2495         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496           gfx.anim_random_frame = last_anim_random_frame;
2497       }
2498     }
2499   }
2500 }
2501
2502 void DisplayGameControlValues()
2503 {
2504   boolean redraw_panel = FALSE;
2505   int i;
2506
2507   for (i = 0; game_panel_controls[i].nr != -1; i++)
2508   {
2509     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2510
2511     if (PANEL_DEACTIVATED(gpc->pos))
2512       continue;
2513
2514     if (gpc->value == gpc->last_value &&
2515         gpc->frame == gpc->last_frame)
2516       continue;
2517
2518     redraw_panel = TRUE;
2519   }
2520
2521   if (!redraw_panel)
2522     return;
2523
2524   /* copy default game door content to main double buffer */
2525
2526   /* !!! CHECK AGAIN !!! */
2527   SetPanelBackground();
2528   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2529   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2530
2531   /* redraw game control buttons */
2532   RedrawGameButtons();
2533
2534   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2535
2536   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2537   {
2538     int nr = game_panel_order[i].nr;
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2540     struct TextPosInfo *pos = gpc->pos;
2541     int type = gpc->type;
2542     int value = gpc->value;
2543     int frame = gpc->frame;
2544     int size = pos->size;
2545     int font = pos->font;
2546     boolean draw_masked = pos->draw_masked;
2547     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2548
2549     if (PANEL_DEACTIVATED(pos))
2550       continue;
2551
2552     gpc->last_value = value;
2553     gpc->last_frame = frame;
2554
2555     if (type == TYPE_INTEGER)
2556     {
2557       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2558           nr == GAME_PANEL_TIME)
2559       {
2560         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2561
2562         if (use_dynamic_size)           /* use dynamic number of digits */
2563         {
2564           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2565           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2566           int size2 = size1 + 1;
2567           int font1 = pos->font;
2568           int font2 = pos->font_alt;
2569
2570           size = (value < value_change ? size1 : size2);
2571           font = (value < value_change ? font1 : font2);
2572         }
2573       }
2574
2575       /* correct text size if "digits" is zero or less */
2576       if (size <= 0)
2577         size = strlen(int2str(value, size));
2578
2579       /* dynamically correct text alignment */
2580       pos->width = size * getFontWidth(font);
2581
2582       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2583                   int2str(value, size), font, mask_mode);
2584     }
2585     else if (type == TYPE_ELEMENT)
2586     {
2587       int element, graphic;
2588       Bitmap *src_bitmap;
2589       int src_x, src_y;
2590       int width, height;
2591       int dst_x = PANEL_XPOS(pos);
2592       int dst_y = PANEL_YPOS(pos);
2593
2594       if (value != EL_UNDEFINED && value != EL_EMPTY)
2595       {
2596         element = value;
2597         graphic = el2panelimg(value);
2598
2599         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2600
2601         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2602           size = TILESIZE;
2603
2604         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2605                               &src_x, &src_y);
2606
2607         width  = graphic_info[graphic].width  * size / TILESIZE;
2608         height = graphic_info[graphic].height * size / TILESIZE;
2609
2610         if (draw_masked)
2611           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2612                            dst_x, dst_y);
2613         else
2614           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2615                      dst_x, dst_y);
2616       }
2617     }
2618     else if (type == TYPE_GRAPHIC)
2619     {
2620       int graphic        = gpc->graphic;
2621       int graphic_active = gpc->graphic_active;
2622       Bitmap *src_bitmap;
2623       int src_x, src_y;
2624       int width, height;
2625       int dst_x = PANEL_XPOS(pos);
2626       int dst_y = PANEL_YPOS(pos);
2627       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2628                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2629
2630       if (graphic != IMG_UNDEFINED && !skip)
2631       {
2632         if (pos->style == STYLE_REVERSE)
2633           value = 100 - value;
2634
2635         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2636
2637         if (pos->direction & MV_HORIZONTAL)
2638         {
2639           width  = graphic_info[graphic_active].width * value / 100;
2640           height = graphic_info[graphic_active].height;
2641
2642           if (pos->direction == MV_LEFT)
2643           {
2644             src_x += graphic_info[graphic_active].width - width;
2645             dst_x += graphic_info[graphic_active].width - width;
2646           }
2647         }
2648         else
2649         {
2650           width  = graphic_info[graphic_active].width;
2651           height = graphic_info[graphic_active].height * value / 100;
2652
2653           if (pos->direction == MV_UP)
2654           {
2655             src_y += graphic_info[graphic_active].height - height;
2656             dst_y += graphic_info[graphic_active].height - height;
2657           }
2658         }
2659
2660         if (draw_masked)
2661           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2662                            dst_x, dst_y);
2663         else
2664           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2665                      dst_x, dst_y);
2666
2667         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2668
2669         if (pos->direction & MV_HORIZONTAL)
2670         {
2671           if (pos->direction == MV_RIGHT)
2672           {
2673             src_x += width;
2674             dst_x += width;
2675           }
2676           else
2677           {
2678             dst_x = PANEL_XPOS(pos);
2679           }
2680
2681           width = graphic_info[graphic].width - width;
2682         }
2683         else
2684         {
2685           if (pos->direction == MV_DOWN)
2686           {
2687             src_y += height;
2688             dst_y += height;
2689           }
2690           else
2691           {
2692             dst_y = PANEL_YPOS(pos);
2693           }
2694
2695           height = graphic_info[graphic].height - height;
2696         }
2697
2698         if (draw_masked)
2699           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2700                            dst_x, dst_y);
2701         else
2702           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2703                      dst_x, dst_y);
2704       }
2705     }
2706     else if (type == TYPE_STRING)
2707     {
2708       boolean active = (value != 0);
2709       char *state_normal = "off";
2710       char *state_active = "on";
2711       char *state = (active ? state_active : state_normal);
2712       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2713                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2714                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2715                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2716
2717       if (nr == GAME_PANEL_GRAVITY_STATE)
2718       {
2719         int font1 = pos->font;          /* (used for normal state) */
2720         int font2 = pos->font_alt;      /* (used for active state) */
2721
2722         font = (active ? font2 : font1);
2723       }
2724
2725       if (s != NULL)
2726       {
2727         char *s_cut;
2728
2729         if (size <= 0)
2730         {
2731           /* don't truncate output if "chars" is zero or less */
2732           size = strlen(s);
2733
2734           /* dynamically correct text alignment */
2735           pos->width = size * getFontWidth(font);
2736         }
2737
2738         s_cut = getStringCopyN(s, size);
2739
2740         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2741                     s_cut, font, mask_mode);
2742
2743         free(s_cut);
2744       }
2745     }
2746
2747     redraw_mask |= REDRAW_DOOR_1;
2748   }
2749
2750   SetGameStatus(GAME_MODE_PLAYING);
2751 }
2752
2753 void UpdateAndDisplayGameControlValues()
2754 {
2755   if (tape.deactivate_display)
2756     return;
2757
2758   UpdateGameControlValues();
2759   DisplayGameControlValues();
2760 }
2761
2762 void UpdateGameDoorValues()
2763 {
2764   UpdateGameControlValues();
2765 }
2766
2767 void DrawGameDoorValues()
2768 {
2769   DisplayGameControlValues();
2770 }
2771
2772
2773 /*
2774   =============================================================================
2775   InitGameEngine()
2776   -----------------------------------------------------------------------------
2777   initialize game engine due to level / tape version number
2778   =============================================================================
2779 */
2780
2781 static void InitGameEngine()
2782 {
2783   int i, j, k, l, x, y;
2784
2785   /* set game engine from tape file when re-playing, else from level file */
2786   game.engine_version = (tape.playing ? tape.engine_version :
2787                          level.game_version);
2788
2789   /* set single or multi-player game mode (needed for re-playing tapes) */
2790   game.team_mode = setup.team_mode;
2791
2792   if (tape.playing)
2793   {
2794     int num_players = 0;
2795
2796     for (i = 0; i < MAX_PLAYERS; i++)
2797       if (tape.player_participates[i])
2798         num_players++;
2799
2800     /* multi-player tapes contain input data for more than one player */
2801     game.team_mode = (num_players > 1);
2802   }
2803
2804   /* ---------------------------------------------------------------------- */
2805   /* set flags for bugs and changes according to active game engine version */
2806   /* ---------------------------------------------------------------------- */
2807
2808   /*
2809     Summary of bugfix/change:
2810     Fixed handling for custom elements that change when pushed by the player.
2811
2812     Fixed/changed in version:
2813     3.1.0
2814
2815     Description:
2816     Before 3.1.0, custom elements that "change when pushing" changed directly
2817     after the player started pushing them (until then handled in "DigField()").
2818     Since 3.1.0, these custom elements are not changed until the "pushing"
2819     move of the element is finished (now handled in "ContinueMoving()").
2820
2821     Affected levels/tapes:
2822     The first condition is generally needed for all levels/tapes before version
2823     3.1.0, which might use the old behaviour before it was changed; known tapes
2824     that are affected are some tapes from the level set "Walpurgis Gardens" by
2825     Jamie Cullen.
2826     The second condition is an exception from the above case and is needed for
2827     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2828     above (including some development versions of 3.1.0), but before it was
2829     known that this change would break tapes like the above and was fixed in
2830     3.1.1, so that the changed behaviour was active although the engine version
2831     while recording maybe was before 3.1.0. There is at least one tape that is
2832     affected by this exception, which is the tape for the one-level set "Bug
2833     Machine" by Juergen Bonhagen.
2834   */
2835
2836   game.use_change_when_pushing_bug =
2837     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2838      !(tape.playing &&
2839        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2840        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2841
2842   /*
2843     Summary of bugfix/change:
2844     Fixed handling for blocking the field the player leaves when moving.
2845
2846     Fixed/changed in version:
2847     3.1.1
2848
2849     Description:
2850     Before 3.1.1, when "block last field when moving" was enabled, the field
2851     the player is leaving when moving was blocked for the time of the move,
2852     and was directly unblocked afterwards. This resulted in the last field
2853     being blocked for exactly one less than the number of frames of one player
2854     move. Additionally, even when blocking was disabled, the last field was
2855     blocked for exactly one frame.
2856     Since 3.1.1, due to changes in player movement handling, the last field
2857     is not blocked at all when blocking is disabled. When blocking is enabled,
2858     the last field is blocked for exactly the number of frames of one player
2859     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2860     last field is blocked for exactly one more than the number of frames of
2861     one player move.
2862
2863     Affected levels/tapes:
2864     (!!! yet to be determined -- probably many !!!)
2865   */
2866
2867   game.use_block_last_field_bug =
2868     (game.engine_version < VERSION_IDENT(3,1,1,0));
2869
2870   game_em.use_single_button =
2871     (game.engine_version > VERSION_IDENT(4,0,0,2));
2872
2873   game_em.use_snap_key_bug =
2874     (game.engine_version < VERSION_IDENT(4,0,1,0));
2875
2876   /* ---------------------------------------------------------------------- */
2877
2878   /* set maximal allowed number of custom element changes per game frame */
2879   game.max_num_changes_per_frame = 1;
2880
2881   /* default scan direction: scan playfield from top/left to bottom/right */
2882   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2883
2884   /* dynamically adjust element properties according to game engine version */
2885   InitElementPropertiesEngine(game.engine_version);
2886
2887 #if 0
2888   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2889   printf("          tape version == %06d [%s] [file: %06d]\n",
2890          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2891          tape.file_version);
2892   printf("       => game.engine_version == %06d\n", game.engine_version);
2893 #endif
2894
2895   /* ---------- initialize player's initial move delay --------------------- */
2896
2897   /* dynamically adjust player properties according to level information */
2898   for (i = 0; i < MAX_PLAYERS; i++)
2899     game.initial_move_delay_value[i] =
2900       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2901
2902   /* dynamically adjust player properties according to game engine version */
2903   for (i = 0; i < MAX_PLAYERS; i++)
2904     game.initial_move_delay[i] =
2905       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2906        game.initial_move_delay_value[i] : 0);
2907
2908   /* ---------- initialize player's initial push delay --------------------- */
2909
2910   /* dynamically adjust player properties according to game engine version */
2911   game.initial_push_delay_value =
2912     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2913
2914   /* ---------- initialize changing elements ------------------------------- */
2915
2916   /* initialize changing elements information */
2917   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2918   {
2919     struct ElementInfo *ei = &element_info[i];
2920
2921     /* this pointer might have been changed in the level editor */
2922     ei->change = &ei->change_page[0];
2923
2924     if (!IS_CUSTOM_ELEMENT(i))
2925     {
2926       ei->change->target_element = EL_EMPTY_SPACE;
2927       ei->change->delay_fixed = 0;
2928       ei->change->delay_random = 0;
2929       ei->change->delay_frames = 1;
2930     }
2931
2932     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2933     {
2934       ei->has_change_event[j] = FALSE;
2935
2936       ei->event_page_nr[j] = 0;
2937       ei->event_page[j] = &ei->change_page[0];
2938     }
2939   }
2940
2941   /* add changing elements from pre-defined list */
2942   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2943   {
2944     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2945     struct ElementInfo *ei = &element_info[ch_delay->element];
2946
2947     ei->change->target_element       = ch_delay->target_element;
2948     ei->change->delay_fixed          = ch_delay->change_delay;
2949
2950     ei->change->pre_change_function  = ch_delay->pre_change_function;
2951     ei->change->change_function      = ch_delay->change_function;
2952     ei->change->post_change_function = ch_delay->post_change_function;
2953
2954     ei->change->can_change = TRUE;
2955     ei->change->can_change_or_has_action = TRUE;
2956
2957     ei->has_change_event[CE_DELAY] = TRUE;
2958
2959     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2960     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2961   }
2962
2963   /* ---------- initialize internal run-time variables --------------------- */
2964
2965   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2966   {
2967     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2968
2969     for (j = 0; j < ei->num_change_pages; j++)
2970     {
2971       ei->change_page[j].can_change_or_has_action =
2972         (ei->change_page[j].can_change |
2973          ei->change_page[j].has_action);
2974     }
2975   }
2976
2977   /* add change events from custom element configuration */
2978   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2979   {
2980     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2981
2982     for (j = 0; j < ei->num_change_pages; j++)
2983     {
2984       if (!ei->change_page[j].can_change_or_has_action)
2985         continue;
2986
2987       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2988       {
2989         /* only add event page for the first page found with this event */
2990         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2991         {
2992           ei->has_change_event[k] = TRUE;
2993
2994           ei->event_page_nr[k] = j;
2995           ei->event_page[k] = &ei->change_page[j];
2996         }
2997       }
2998     }
2999   }
3000
3001   /* ---------- initialize reference elements in change conditions --------- */
3002
3003   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3004   {
3005     int element = EL_CUSTOM_START + i;
3006     struct ElementInfo *ei = &element_info[element];
3007
3008     for (j = 0; j < ei->num_change_pages; j++)
3009     {
3010       int trigger_element = ei->change_page[j].initial_trigger_element;
3011
3012       if (trigger_element >= EL_PREV_CE_8 &&
3013           trigger_element <= EL_NEXT_CE_8)
3014         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3015
3016       ei->change_page[j].trigger_element = trigger_element;
3017     }
3018   }
3019
3020   /* ---------- initialize run-time trigger player and element ------------- */
3021
3022   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3023   {
3024     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3025
3026     for (j = 0; j < ei->num_change_pages; j++)
3027     {
3028       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3029       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3030       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3031       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3032       ei->change_page[j].actual_trigger_ce_value = 0;
3033       ei->change_page[j].actual_trigger_ce_score = 0;
3034     }
3035   }
3036
3037   /* ---------- initialize trigger events ---------------------------------- */
3038
3039   /* initialize trigger events information */
3040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3041     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3042       trigger_events[i][j] = FALSE;
3043
3044   /* add trigger events from element change event properties */
3045   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3046   {
3047     struct ElementInfo *ei = &element_info[i];
3048
3049     for (j = 0; j < ei->num_change_pages; j++)
3050     {
3051       if (!ei->change_page[j].can_change_or_has_action)
3052         continue;
3053
3054       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3055       {
3056         int trigger_element = ei->change_page[j].trigger_element;
3057
3058         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3059         {
3060           if (ei->change_page[j].has_event[k])
3061           {
3062             if (IS_GROUP_ELEMENT(trigger_element))
3063             {
3064               struct ElementGroupInfo *group =
3065                 element_info[trigger_element].group;
3066
3067               for (l = 0; l < group->num_elements_resolved; l++)
3068                 trigger_events[group->element_resolved[l]][k] = TRUE;
3069             }
3070             else if (trigger_element == EL_ANY_ELEMENT)
3071               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3072                 trigger_events[l][k] = TRUE;
3073             else
3074               trigger_events[trigger_element][k] = TRUE;
3075           }
3076         }
3077       }
3078     }
3079   }
3080
3081   /* ---------- initialize push delay -------------------------------------- */
3082
3083   /* initialize push delay values to default */
3084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3085   {
3086     if (!IS_CUSTOM_ELEMENT(i))
3087     {
3088       /* set default push delay values (corrected since version 3.0.7-1) */
3089       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3090       {
3091         element_info[i].push_delay_fixed = 2;
3092         element_info[i].push_delay_random = 8;
3093       }
3094       else
3095       {
3096         element_info[i].push_delay_fixed = 8;
3097         element_info[i].push_delay_random = 8;
3098       }
3099     }
3100   }
3101
3102   /* set push delay value for certain elements from pre-defined list */
3103   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3104   {
3105     int e = push_delay_list[i].element;
3106
3107     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3108     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3109   }
3110
3111   /* set push delay value for Supaplex elements for newer engine versions */
3112   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3113   {
3114     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3115     {
3116       if (IS_SP_ELEMENT(i))
3117       {
3118         /* set SP push delay to just enough to push under a falling zonk */
3119         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3120
3121         element_info[i].push_delay_fixed  = delay;
3122         element_info[i].push_delay_random = 0;
3123       }
3124     }
3125   }
3126
3127   /* ---------- initialize move stepsize ----------------------------------- */
3128
3129   /* initialize move stepsize values to default */
3130   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3131     if (!IS_CUSTOM_ELEMENT(i))
3132       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3133
3134   /* set move stepsize value for certain elements from pre-defined list */
3135   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3136   {
3137     int e = move_stepsize_list[i].element;
3138
3139     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3140   }
3141
3142   /* ---------- initialize collect score ----------------------------------- */
3143
3144   /* initialize collect score values for custom elements from initial value */
3145   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3146     if (IS_CUSTOM_ELEMENT(i))
3147       element_info[i].collect_score = element_info[i].collect_score_initial;
3148
3149   /* ---------- initialize collect count ----------------------------------- */
3150
3151   /* initialize collect count values for non-custom elements */
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153     if (!IS_CUSTOM_ELEMENT(i))
3154       element_info[i].collect_count_initial = 0;
3155
3156   /* add collect count values for all elements from pre-defined list */
3157   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3158     element_info[collect_count_list[i].element].collect_count_initial =
3159       collect_count_list[i].count;
3160
3161   /* ---------- initialize access direction -------------------------------- */
3162
3163   /* initialize access direction values to default (access from every side) */
3164   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3165     if (!IS_CUSTOM_ELEMENT(i))
3166       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3167
3168   /* set access direction value for certain elements from pre-defined list */
3169   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3170     element_info[access_direction_list[i].element].access_direction =
3171       access_direction_list[i].direction;
3172
3173   /* ---------- initialize explosion content ------------------------------- */
3174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3175   {
3176     if (IS_CUSTOM_ELEMENT(i))
3177       continue;
3178
3179     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3180     {
3181       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3182
3183       element_info[i].content.e[x][y] =
3184         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3185          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3186          i == EL_PLAYER_3 ? EL_EMERALD :
3187          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3188          i == EL_MOLE ? EL_EMERALD_RED :
3189          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3190          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3191          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3192          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3193          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3194          i == EL_WALL_EMERALD ? EL_EMERALD :
3195          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3196          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3197          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3198          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3199          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3200          i == EL_WALL_PEARL ? EL_PEARL :
3201          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3202          EL_EMPTY);
3203     }
3204   }
3205
3206   /* ---------- initialize recursion detection ------------------------------ */
3207   recursion_loop_depth = 0;
3208   recursion_loop_detected = FALSE;
3209   recursion_loop_element = EL_UNDEFINED;
3210
3211   /* ---------- initialize graphics engine ---------------------------------- */
3212   game.scroll_delay_value =
3213     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3214      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3215   game.scroll_delay_value =
3216     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3217
3218   /* ---------- initialize game engine snapshots ---------------------------- */
3219   for (i = 0; i < MAX_PLAYERS; i++)
3220     game.snapshot.last_action[i] = 0;
3221   game.snapshot.changed_action = FALSE;
3222   game.snapshot.collected_item = FALSE;
3223   game.snapshot.mode =
3224     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3225      SNAPSHOT_MODE_EVERY_STEP :
3226      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3227      SNAPSHOT_MODE_EVERY_MOVE :
3228      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3229      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3230   game.snapshot.save_snapshot = FALSE;
3231
3232   /* ---------- initialize level time for Supaplex engine ------------------- */
3233   /* Supaplex levels with time limit currently unsupported -- should be added */
3234   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3235     level.time = 0;
3236 }
3237
3238 int get_num_special_action(int element, int action_first, int action_last)
3239 {
3240   int num_special_action = 0;
3241   int i, j;
3242
3243   for (i = action_first; i <= action_last; i++)
3244   {
3245     boolean found = FALSE;
3246
3247     for (j = 0; j < NUM_DIRECTIONS; j++)
3248       if (el_act_dir2img(element, i, j) !=
3249           el_act_dir2img(element, ACTION_DEFAULT, j))
3250         found = TRUE;
3251
3252     if (found)
3253       num_special_action++;
3254     else
3255       break;
3256   }
3257
3258   return num_special_action;
3259 }
3260
3261
3262 /*
3263   =============================================================================
3264   InitGame()
3265   -----------------------------------------------------------------------------
3266   initialize and start new game
3267   =============================================================================
3268 */
3269
3270 void InitGame()
3271 {
3272   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3273   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3274   int fade_mask = REDRAW_FIELD;
3275
3276   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3277   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3278   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3279   int initial_move_dir = MV_DOWN;
3280   int i, j, x, y;
3281
3282   // required here to update video display before fading (FIX THIS)
3283   DrawMaskedBorder(REDRAW_DOOR_2);
3284
3285   if (!game.restart_level)
3286     CloseDoor(DOOR_CLOSE_1);
3287
3288   SetGameStatus(GAME_MODE_PLAYING);
3289
3290   if (level_editor_test_game)
3291     FadeSkipNextFadeIn();
3292   else
3293     FadeSetEnterScreen();
3294
3295   if (CheckIfGlobalBorderHasChanged())
3296     fade_mask = REDRAW_ALL;
3297
3298   FadeLevelSoundsAndMusic();
3299
3300   ExpireSoundLoops(TRUE);
3301
3302   FadeOut(fade_mask);
3303
3304   /* needed if different viewport properties defined for playing */
3305   ChangeViewportPropertiesIfNeeded();
3306
3307   ClearField();
3308
3309   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3310
3311   DrawCompleteVideoDisplay();
3312
3313   InitGameEngine();
3314   InitGameControlValues();
3315
3316   /* don't play tapes over network */
3317   network_playing = (options.network && !tape.playing);
3318
3319   for (i = 0; i < MAX_PLAYERS; i++)
3320   {
3321     struct PlayerInfo *player = &stored_player[i];
3322
3323     player->index_nr = i;
3324     player->index_bit = (1 << i);
3325     player->element_nr = EL_PLAYER_1 + i;
3326
3327     player->present = FALSE;
3328     player->active = FALSE;
3329     player->mapped = FALSE;
3330
3331     player->killed = FALSE;
3332     player->reanimated = FALSE;
3333
3334     player->action = 0;
3335     player->effective_action = 0;
3336     player->programmed_action = 0;
3337
3338     player->mouse_action.lx = 0;
3339     player->mouse_action.ly = 0;
3340     player->mouse_action.button = 0;
3341
3342     player->effective_mouse_action.lx = 0;
3343     player->effective_mouse_action.ly = 0;
3344     player->effective_mouse_action.button = 0;
3345
3346     player->score = 0;
3347     player->score_final = 0;
3348
3349     player->health = MAX_HEALTH;
3350     player->health_final = MAX_HEALTH;
3351
3352     player->gems_still_needed = level.gems_needed;
3353     player->sokobanfields_still_needed = 0;
3354     player->lights_still_needed = 0;
3355     player->friends_still_needed = 0;
3356
3357     for (j = 0; j < MAX_NUM_KEYS; j++)
3358       player->key[j] = FALSE;
3359
3360     player->num_white_keys = 0;
3361
3362     player->dynabomb_count = 0;
3363     player->dynabomb_size = 1;
3364     player->dynabombs_left = 0;
3365     player->dynabomb_xl = FALSE;
3366
3367     player->MovDir = initial_move_dir;
3368     player->MovPos = 0;
3369     player->GfxPos = 0;
3370     player->GfxDir = initial_move_dir;
3371     player->GfxAction = ACTION_DEFAULT;
3372     player->Frame = 0;
3373     player->StepFrame = 0;
3374
3375     player->initial_element = player->element_nr;
3376     player->artwork_element =
3377       (level.use_artwork_element[i] ? level.artwork_element[i] :
3378        player->element_nr);
3379     player->use_murphy = FALSE;
3380
3381     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3382     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3383
3384     player->gravity = level.initial_player_gravity[i];
3385
3386     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3387
3388     player->actual_frame_counter = 0;
3389
3390     player->step_counter = 0;
3391
3392     player->last_move_dir = initial_move_dir;
3393
3394     player->is_active = FALSE;
3395
3396     player->is_waiting = FALSE;
3397     player->is_moving = FALSE;
3398     player->is_auto_moving = FALSE;
3399     player->is_digging = FALSE;
3400     player->is_snapping = FALSE;
3401     player->is_collecting = FALSE;
3402     player->is_pushing = FALSE;
3403     player->is_switching = FALSE;
3404     player->is_dropping = FALSE;
3405     player->is_dropping_pressed = FALSE;
3406
3407     player->is_bored = FALSE;
3408     player->is_sleeping = FALSE;
3409
3410     player->was_waiting = TRUE;
3411     player->was_moving = FALSE;
3412     player->was_snapping = FALSE;
3413     player->was_dropping = FALSE;
3414
3415     player->force_dropping = FALSE;
3416
3417     player->frame_counter_bored = -1;
3418     player->frame_counter_sleeping = -1;
3419
3420     player->anim_delay_counter = 0;
3421     player->post_delay_counter = 0;
3422
3423     player->dir_waiting = initial_move_dir;
3424     player->action_waiting = ACTION_DEFAULT;
3425     player->last_action_waiting = ACTION_DEFAULT;
3426     player->special_action_bored = ACTION_DEFAULT;
3427     player->special_action_sleeping = ACTION_DEFAULT;
3428
3429     player->switch_x = -1;
3430     player->switch_y = -1;
3431
3432     player->drop_x = -1;
3433     player->drop_y = -1;
3434
3435     player->show_envelope = 0;
3436
3437     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3438
3439     player->push_delay       = -1;      /* initialized when pushing starts */
3440     player->push_delay_value = game.initial_push_delay_value;
3441
3442     player->drop_delay = 0;
3443     player->drop_pressed_delay = 0;
3444
3445     player->last_jx = -1;
3446     player->last_jy = -1;
3447     player->jx = -1;
3448     player->jy = -1;
3449
3450     player->shield_normal_time_left = 0;
3451     player->shield_deadly_time_left = 0;
3452
3453     player->inventory_infinite_element = EL_UNDEFINED;
3454     player->inventory_size = 0;
3455
3456     if (level.use_initial_inventory[i])
3457     {
3458       for (j = 0; j < level.initial_inventory_size[i]; j++)
3459       {
3460         int element = level.initial_inventory_content[i][j];
3461         int collect_count = element_info[element].collect_count_initial;
3462         int k;
3463
3464         if (!IS_CUSTOM_ELEMENT(element))
3465           collect_count = 1;
3466
3467         if (collect_count == 0)
3468           player->inventory_infinite_element = element;
3469         else
3470           for (k = 0; k < collect_count; k++)
3471             if (player->inventory_size < MAX_INVENTORY_SIZE)
3472               player->inventory_element[player->inventory_size++] = element;
3473       }
3474     }
3475
3476     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3477     SnapField(player, 0, 0);
3478
3479     player->LevelSolved = FALSE;
3480     player->GameOver = FALSE;
3481
3482     player->LevelSolved_GameWon = FALSE;
3483     player->LevelSolved_GameEnd = FALSE;
3484     player->LevelSolved_PanelOff = FALSE;
3485     player->LevelSolved_SaveTape = FALSE;
3486     player->LevelSolved_SaveScore = FALSE;
3487
3488     player->LevelSolved_CountingTime = 0;
3489     player->LevelSolved_CountingScore = 0;
3490     player->LevelSolved_CountingHealth = 0;
3491
3492     map_player_action[i] = i;
3493   }
3494
3495   network_player_action_received = FALSE;
3496
3497 #if defined(NETWORK_AVALIABLE)
3498   /* initial null action */
3499   if (network_playing)
3500     SendToServer_MovePlayer(MV_NONE);
3501 #endif
3502
3503   ZX = ZY = -1;
3504   ExitX = ExitY = -1;
3505
3506   FrameCounter = 0;
3507   TimeFrames = 0;
3508   TimePlayed = 0;
3509   TimeLeft = level.time;
3510   TapeTime = 0;
3511
3512   ScreenMovDir = MV_NONE;
3513   ScreenMovPos = 0;
3514   ScreenGfxPos = 0;
3515
3516   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3517
3518   AllPlayersGone = FALSE;
3519
3520   game.no_time_limit = (level.time == 0);
3521
3522   game.yamyam_content_nr = 0;
3523   game.robot_wheel_active = FALSE;
3524   game.magic_wall_active = FALSE;
3525   game.magic_wall_time_left = 0;
3526   game.light_time_left = 0;
3527   game.timegate_time_left = 0;
3528   game.switchgate_pos = 0;
3529   game.wind_direction = level.wind_direction_initial;
3530
3531   game.lenses_time_left = 0;
3532   game.magnify_time_left = 0;
3533
3534   game.ball_state = level.ball_state_initial;
3535   game.ball_content_nr = 0;
3536
3537   game.envelope_active = FALSE;
3538
3539   /* set focus to local player for network games, else to all players */
3540   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3541   game.centered_player_nr_next = game.centered_player_nr;
3542   game.set_centered_player = FALSE;
3543
3544   if (network_playing && tape.recording)
3545   {
3546     /* store client dependent player focus when recording network games */
3547     tape.centered_player_nr_next = game.centered_player_nr_next;
3548     tape.set_centered_player = TRUE;
3549   }
3550
3551   for (i = 0; i < NUM_BELTS; i++)
3552   {
3553     game.belt_dir[i] = MV_NONE;
3554     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3555   }
3556
3557   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3558     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3559
3560 #if DEBUG_INIT_PLAYER
3561   if (options.debug)
3562   {
3563     printf("Player status at level initialization:\n");
3564   }
3565 #endif
3566
3567   SCAN_PLAYFIELD(x, y)
3568   {
3569     Feld[x][y] = level.field[x][y];
3570     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3571     ChangeDelay[x][y] = 0;
3572     ChangePage[x][y] = -1;
3573     CustomValue[x][y] = 0;              /* initialized in InitField() */
3574     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3575     AmoebaNr[x][y] = 0;
3576     WasJustMoving[x][y] = 0;
3577     WasJustFalling[x][y] = 0;
3578     CheckCollision[x][y] = 0;
3579     CheckImpact[x][y] = 0;
3580     Stop[x][y] = FALSE;
3581     Pushed[x][y] = FALSE;
3582
3583     ChangeCount[x][y] = 0;
3584     ChangeEvent[x][y] = -1;
3585
3586     ExplodePhase[x][y] = 0;
3587     ExplodeDelay[x][y] = 0;
3588     ExplodeField[x][y] = EX_TYPE_NONE;
3589
3590     RunnerVisit[x][y] = 0;
3591     PlayerVisit[x][y] = 0;
3592
3593     GfxFrame[x][y] = 0;
3594     GfxRandom[x][y] = INIT_GFX_RANDOM();
3595     GfxElement[x][y] = EL_UNDEFINED;
3596     GfxAction[x][y] = ACTION_DEFAULT;
3597     GfxDir[x][y] = MV_NONE;
3598     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3599   }
3600
3601   SCAN_PLAYFIELD(x, y)
3602   {
3603     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3604       emulate_bd = FALSE;
3605     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3606       emulate_sb = FALSE;
3607     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3608       emulate_sp = FALSE;
3609
3610     InitField(x, y, TRUE);
3611
3612     ResetGfxAnimation(x, y);
3613   }
3614
3615   InitBeltMovement();
3616
3617   for (i = 0; i < MAX_PLAYERS; i++)
3618   {
3619     struct PlayerInfo *player = &stored_player[i];
3620
3621     /* set number of special actions for bored and sleeping animation */
3622     player->num_special_action_bored =
3623       get_num_special_action(player->artwork_element,
3624                              ACTION_BORING_1, ACTION_BORING_LAST);
3625     player->num_special_action_sleeping =
3626       get_num_special_action(player->artwork_element,
3627                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3628   }
3629
3630   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3631                     emulate_sb ? EMU_SOKOBAN :
3632                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3633
3634   /* initialize type of slippery elements */
3635   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3636   {
3637     if (!IS_CUSTOM_ELEMENT(i))
3638     {
3639       /* default: elements slip down either to the left or right randomly */
3640       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3641
3642       /* SP style elements prefer to slip down on the left side */
3643       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3644         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3645
3646       /* BD style elements prefer to slip down on the left side */
3647       if (game.emulation == EMU_BOULDERDASH)
3648         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3649     }
3650   }
3651
3652   /* initialize explosion and ignition delay */
3653   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3654   {
3655     if (!IS_CUSTOM_ELEMENT(i))
3656     {
3657       int num_phase = 8;
3658       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3659                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3660                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3661       int last_phase = (num_phase + 1) * delay;
3662       int half_phase = (num_phase / 2) * delay;
3663
3664       element_info[i].explosion_delay = last_phase - 1;
3665       element_info[i].ignition_delay = half_phase;
3666
3667       if (i == EL_BLACK_ORB)
3668         element_info[i].ignition_delay = 1;
3669     }
3670   }
3671
3672   /* correct non-moving belts to start moving left */
3673   for (i = 0; i < NUM_BELTS; i++)
3674     if (game.belt_dir[i] == MV_NONE)
3675       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3676
3677 #if USE_NEW_PLAYER_ASSIGNMENTS
3678   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3679   /* choose default local player */
3680   local_player = &stored_player[0];
3681
3682   for (i = 0; i < MAX_PLAYERS; i++)
3683     stored_player[i].connected = FALSE;
3684
3685   local_player->connected = TRUE;
3686   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3687
3688   if (tape.playing)
3689   {
3690     for (i = 0; i < MAX_PLAYERS; i++)
3691       stored_player[i].connected = tape.player_participates[i];
3692   }
3693   else if (game.team_mode && !options.network)
3694   {
3695     /* try to guess locally connected team mode players (needed for correct
3696        assignment of player figures from level to locally playing players) */
3697
3698     for (i = 0; i < MAX_PLAYERS; i++)
3699       if (setup.input[i].use_joystick ||
3700           setup.input[i].key.left != KSYM_UNDEFINED)
3701         stored_player[i].connected = TRUE;
3702   }
3703
3704 #if DEBUG_INIT_PLAYER
3705   if (options.debug)
3706   {
3707     printf("Player status after level initialization:\n");
3708
3709     for (i = 0; i < MAX_PLAYERS; i++)
3710     {
3711       struct PlayerInfo *player = &stored_player[i];
3712
3713       printf("- player %d: present == %d, connected == %d, active == %d",
3714              i + 1,
3715              player->present,
3716              player->connected,
3717              player->active);
3718
3719       if (local_player == player)
3720         printf(" (local player)");
3721
3722       printf("\n");
3723     }
3724   }
3725 #endif
3726
3727 #if DEBUG_INIT_PLAYER
3728   if (options.debug)
3729     printf("Reassigning players ...\n");
3730 #endif
3731
3732   /* check if any connected player was not found in playfield */
3733   for (i = 0; i < MAX_PLAYERS; i++)
3734   {
3735     struct PlayerInfo *player = &stored_player[i];
3736
3737     if (player->connected && !player->present)
3738     {
3739       struct PlayerInfo *field_player = NULL;
3740
3741 #if DEBUG_INIT_PLAYER
3742       if (options.debug)
3743         printf("- looking for field player for player %d ...\n", i + 1);
3744 #endif
3745
3746       /* assign first free player found that is present in the playfield */
3747
3748       /* first try: look for unmapped playfield player that is not connected */
3749       for (j = 0; j < MAX_PLAYERS; j++)
3750         if (field_player == NULL &&
3751             stored_player[j].present &&
3752             !stored_player[j].mapped &&
3753             !stored_player[j].connected)
3754           field_player = &stored_player[j];
3755
3756       /* second try: look for *any* unmapped playfield player */
3757       for (j = 0; j < MAX_PLAYERS; j++)
3758         if (field_player == NULL &&
3759             stored_player[j].present &&
3760             !stored_player[j].mapped)
3761           field_player = &stored_player[j];
3762
3763       if (field_player != NULL)
3764       {
3765         int jx = field_player->jx, jy = field_player->jy;
3766
3767 #if DEBUG_INIT_PLAYER
3768         if (options.debug)
3769           printf("- found player %d\n", field_player->index_nr + 1);
3770 #endif
3771
3772         player->present = FALSE;
3773         player->active = FALSE;
3774
3775         field_player->present = TRUE;
3776         field_player->active = TRUE;
3777
3778         /*
3779         player->initial_element = field_player->initial_element;
3780         player->artwork_element = field_player->artwork_element;
3781
3782         player->block_last_field       = field_player->block_last_field;
3783         player->block_delay_adjustment = field_player->block_delay_adjustment;
3784         */
3785
3786         StorePlayer[jx][jy] = field_player->element_nr;
3787
3788         field_player->jx = field_player->last_jx = jx;
3789         field_player->jy = field_player->last_jy = jy;
3790
3791         if (local_player == player)
3792           local_player = field_player;
3793
3794         map_player_action[field_player->index_nr] = i;
3795
3796         field_player->mapped = TRUE;
3797
3798 #if DEBUG_INIT_PLAYER
3799         if (options.debug)
3800           printf("- map_player_action[%d] == %d\n",
3801                  field_player->index_nr + 1, i + 1);
3802 #endif
3803       }
3804     }
3805
3806     if (player->connected && player->present)
3807       player->mapped = TRUE;
3808   }
3809
3810 #if DEBUG_INIT_PLAYER
3811   if (options.debug)
3812   {
3813     printf("Player status after player assignment (first stage):\n");
3814
3815     for (i = 0; i < MAX_PLAYERS; i++)
3816     {
3817       struct PlayerInfo *player = &stored_player[i];
3818
3819       printf("- player %d: present == %d, connected == %d, active == %d",
3820              i + 1,
3821              player->present,
3822              player->connected,
3823              player->active);
3824
3825       if (local_player == player)
3826         printf(" (local player)");
3827
3828       printf("\n");
3829     }
3830   }
3831 #endif
3832
3833 #else
3834
3835   /* check if any connected player was not found in playfield */
3836   for (i = 0; i < MAX_PLAYERS; i++)
3837   {
3838     struct PlayerInfo *player = &stored_player[i];
3839
3840     if (player->connected && !player->present)
3841     {
3842       for (j = 0; j < MAX_PLAYERS; j++)
3843       {
3844         struct PlayerInfo *field_player = &stored_player[j];
3845         int jx = field_player->jx, jy = field_player->jy;
3846
3847         /* assign first free player found that is present in the playfield */
3848         if (field_player->present && !field_player->connected)
3849         {
3850           player->present = TRUE;
3851           player->active = TRUE;
3852
3853           field_player->present = FALSE;
3854           field_player->active = FALSE;
3855
3856           player->initial_element = field_player->initial_element;
3857           player->artwork_element = field_player->artwork_element;
3858
3859           player->block_last_field       = field_player->block_last_field;
3860           player->block_delay_adjustment = field_player->block_delay_adjustment;
3861
3862           StorePlayer[jx][jy] = player->element_nr;
3863
3864           player->jx = player->last_jx = jx;
3865           player->jy = player->last_jy = jy;
3866
3867           break;
3868         }
3869       }
3870     }
3871   }
3872 #endif
3873
3874 #if 0
3875   printf("::: local_player->present == %d\n", local_player->present);
3876 #endif
3877
3878   if (tape.playing)
3879   {
3880     /* when playing a tape, eliminate all players who do not participate */
3881
3882 #if USE_NEW_PLAYER_ASSIGNMENTS
3883
3884     if (!game.team_mode)
3885     {
3886       for (i = 0; i < MAX_PLAYERS; i++)
3887       {
3888         if (stored_player[i].active &&
3889             !tape.player_participates[map_player_action[i]])
3890         {
3891           struct PlayerInfo *player = &stored_player[i];
3892           int jx = player->jx, jy = player->jy;
3893
3894 #if DEBUG_INIT_PLAYER
3895           if (options.debug)
3896             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3897 #endif
3898
3899           player->active = FALSE;
3900           StorePlayer[jx][jy] = 0;
3901           Feld[jx][jy] = EL_EMPTY;
3902         }
3903       }
3904     }
3905
3906 #else
3907
3908     for (i = 0; i < MAX_PLAYERS; i++)
3909     {
3910       if (stored_player[i].active &&
3911           !tape.player_participates[i])
3912       {
3913         struct PlayerInfo *player = &stored_player[i];
3914         int jx = player->jx, jy = player->jy;
3915
3916         player->active = FALSE;
3917         StorePlayer[jx][jy] = 0;
3918         Feld[jx][jy] = EL_EMPTY;
3919       }
3920     }
3921 #endif
3922   }
3923   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3924   {
3925     /* when in single player mode, eliminate all but the first active player */
3926
3927     for (i = 0; i < MAX_PLAYERS; i++)
3928     {
3929       if (stored_player[i].active)
3930       {
3931         for (j = i + 1; j < MAX_PLAYERS; j++)
3932         {
3933           if (stored_player[j].active)
3934           {
3935             struct PlayerInfo *player = &stored_player[j];
3936             int jx = player->jx, jy = player->jy;
3937
3938             player->active = FALSE;
3939             player->present = FALSE;
3940
3941             StorePlayer[jx][jy] = 0;
3942             Feld[jx][jy] = EL_EMPTY;
3943           }
3944         }
3945       }
3946     }
3947   }
3948
3949   /* when recording the game, store which players take part in the game */
3950   if (tape.recording)
3951   {
3952 #if USE_NEW_PLAYER_ASSIGNMENTS
3953     for (i = 0; i < MAX_PLAYERS; i++)
3954       if (stored_player[i].connected)
3955         tape.player_participates[i] = TRUE;
3956 #else
3957     for (i = 0; i < MAX_PLAYERS; i++)
3958       if (stored_player[i].active)
3959         tape.player_participates[i] = TRUE;
3960 #endif
3961   }
3962
3963 #if DEBUG_INIT_PLAYER
3964   if (options.debug)
3965   {
3966     printf("Player status after player assignment (final stage):\n");
3967
3968     for (i = 0; i < MAX_PLAYERS; i++)
3969     {
3970       struct PlayerInfo *player = &stored_player[i];
3971
3972       printf("- player %d: present == %d, connected == %d, active == %d",
3973              i + 1,
3974              player->present,
3975              player->connected,
3976              player->active);
3977
3978       if (local_player == player)
3979         printf(" (local player)");
3980
3981       printf("\n");
3982     }
3983   }
3984 #endif
3985
3986   if (BorderElement == EL_EMPTY)
3987   {
3988     SBX_Left = 0;
3989     SBX_Right = lev_fieldx - SCR_FIELDX;
3990     SBY_Upper = 0;
3991     SBY_Lower = lev_fieldy - SCR_FIELDY;
3992   }
3993   else
3994   {
3995     SBX_Left = -1;
3996     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3997     SBY_Upper = -1;
3998     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3999   }
4000
4001   if (full_lev_fieldx <= SCR_FIELDX)
4002     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4003   if (full_lev_fieldy <= SCR_FIELDY)
4004     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4005
4006   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4007     SBX_Left--;
4008   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4009     SBY_Upper--;
4010
4011   /* if local player not found, look for custom element that might create
4012      the player (make some assumptions about the right custom element) */
4013   if (!local_player->present)
4014   {
4015     int start_x = 0, start_y = 0;
4016     int found_rating = 0;
4017     int found_element = EL_UNDEFINED;
4018     int player_nr = local_player->index_nr;
4019
4020     SCAN_PLAYFIELD(x, y)
4021     {
4022       int element = Feld[x][y];
4023       int content;
4024       int xx, yy;
4025       boolean is_player;
4026
4027       if (level.use_start_element[player_nr] &&
4028           level.start_element[player_nr] == element &&
4029           found_rating < 4)
4030       {
4031         start_x = x;
4032         start_y = y;
4033
4034         found_rating = 4;
4035         found_element = element;
4036       }
4037
4038       if (!IS_CUSTOM_ELEMENT(element))
4039         continue;
4040
4041       if (CAN_CHANGE(element))
4042       {
4043         for (i = 0; i < element_info[element].num_change_pages; i++)
4044         {
4045           /* check for player created from custom element as single target */
4046           content = element_info[element].change_page[i].target_element;
4047           is_player = ELEM_IS_PLAYER(content);
4048
4049           if (is_player && (found_rating < 3 ||
4050                             (found_rating == 3 && element < found_element)))
4051           {
4052             start_x = x;
4053             start_y = y;
4054
4055             found_rating = 3;
4056             found_element = element;
4057           }
4058         }
4059       }
4060
4061       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4062       {
4063         /* check for player created from custom element as explosion content */
4064         content = element_info[element].content.e[xx][yy];
4065         is_player = ELEM_IS_PLAYER(content);
4066
4067         if (is_player && (found_rating < 2 ||
4068                           (found_rating == 2 && element < found_element)))
4069         {
4070           start_x = x + xx - 1;
4071           start_y = y + yy - 1;
4072
4073           found_rating = 2;
4074           found_element = element;
4075         }
4076
4077         if (!CAN_CHANGE(element))
4078           continue;
4079
4080         for (i = 0; i < element_info[element].num_change_pages; i++)
4081         {
4082           /* check for player created from custom element as extended target */
4083           content =
4084             element_info[element].change_page[i].target_content.e[xx][yy];
4085
4086           is_player = ELEM_IS_PLAYER(content);
4087
4088           if (is_player && (found_rating < 1 ||
4089                             (found_rating == 1 && element < found_element)))
4090           {
4091             start_x = x + xx - 1;
4092             start_y = y + yy - 1;
4093
4094             found_rating = 1;
4095             found_element = element;
4096           }
4097         }
4098       }
4099     }
4100
4101     scroll_x = SCROLL_POSITION_X(start_x);
4102     scroll_y = SCROLL_POSITION_Y(start_y);
4103   }
4104   else
4105   {
4106     scroll_x = SCROLL_POSITION_X(local_player->jx);
4107     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4108   }
4109
4110   /* !!! FIX THIS (START) !!! */
4111   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4112   {
4113     InitGameEngine_EM();
4114   }
4115   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4116   {
4117     InitGameEngine_SP();
4118   }
4119   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4120   {
4121     InitGameEngine_MM();
4122   }
4123   else
4124   {
4125     DrawLevel(REDRAW_FIELD);
4126     DrawAllPlayers();
4127
4128     /* after drawing the level, correct some elements */
4129     if (game.timegate_time_left == 0)
4130       CloseAllOpenTimegates();
4131   }
4132
4133   /* blit playfield from scroll buffer to normal back buffer for fading in */
4134   BlitScreenToBitmap(backbuffer);
4135   /* !!! FIX THIS (END) !!! */
4136
4137   DrawMaskedBorder(fade_mask);
4138
4139   FadeIn(fade_mask);
4140
4141 #if 1
4142   // full screen redraw is required at this point in the following cases:
4143   // - special editor door undrawn when game was started from level editor
4144   // - drawing area (playfield) was changed and has to be removed completely
4145   redraw_mask = REDRAW_ALL;
4146   BackToFront();
4147 #endif
4148
4149   if (!game.restart_level)
4150   {
4151     /* copy default game door content to main double buffer */
4152
4153     /* !!! CHECK AGAIN !!! */
4154     SetPanelBackground();
4155     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4156     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4157   }
4158
4159   SetPanelBackground();
4160   SetDrawBackgroundMask(REDRAW_DOOR_1);
4161
4162   UpdateAndDisplayGameControlValues();
4163
4164   if (!game.restart_level)
4165   {
4166     UnmapGameButtons();
4167     UnmapTapeButtons();
4168
4169     FreeGameButtons();
4170     CreateGameButtons();
4171
4172     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4173     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4174     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4175
4176     MapGameButtons();
4177     MapTapeButtons();
4178
4179     /* copy actual game door content to door double buffer for OpenDoor() */
4180     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4181
4182     OpenDoor(DOOR_OPEN_ALL);
4183
4184     PlaySound(SND_GAME_STARTING);
4185
4186     if (setup.sound_music)
4187       PlayLevelMusic();
4188
4189     KeyboardAutoRepeatOffUnlessAutoplay();
4190
4191 #if DEBUG_INIT_PLAYER
4192     if (options.debug)
4193     {
4194       printf("Player status (final):\n");
4195
4196       for (i = 0; i < MAX_PLAYERS; i++)
4197       {
4198         struct PlayerInfo *player = &stored_player[i];
4199
4200         printf("- player %d: present == %d, connected == %d, active == %d",
4201                i + 1,
4202                player->present,
4203                player->connected,
4204                player->active);
4205
4206         if (local_player == player)
4207           printf(" (local player)");
4208
4209         printf("\n");
4210       }
4211     }
4212 #endif
4213   }
4214
4215   UnmapAllGadgets();
4216
4217   MapGameButtons();
4218   MapTapeButtons();
4219
4220   if (!game.restart_level && !tape.playing)
4221   {
4222     LevelStats_incPlayed(level_nr);
4223
4224     SaveLevelSetup_SeriesInfo();
4225   }
4226
4227   game.restart_level = FALSE;
4228
4229   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4230     InitGameActions_MM();
4231
4232   SaveEngineSnapshotToListInitial();
4233 }
4234
4235 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4236                         int actual_player_x, int actual_player_y)
4237 {
4238   /* this is used for non-R'n'D game engines to update certain engine values */
4239
4240   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4241   {
4242     actual_player_x = correctLevelPosX_EM(actual_player_x);
4243     actual_player_y = correctLevelPosY_EM(actual_player_y);
4244   }
4245
4246   /* needed to determine if sounds are played within the visible screen area */
4247   scroll_x = actual_scroll_x;
4248   scroll_y = actual_scroll_y;
4249
4250   /* needed to get player position for "follow finger" playing input method */
4251   local_player->jx = actual_player_x;
4252   local_player->jy = actual_player_y;
4253 }
4254
4255 void InitMovDir(int x, int y)
4256 {
4257   int i, element = Feld[x][y];
4258   static int xy[4][2] =
4259   {
4260     {  0, +1 },
4261     { +1,  0 },
4262     {  0, -1 },
4263     { -1,  0 }
4264   };
4265   static int direction[3][4] =
4266   {
4267     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4268     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4269     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4270   };
4271
4272   switch (element)
4273   {
4274     case EL_BUG_RIGHT:
4275     case EL_BUG_UP:
4276     case EL_BUG_LEFT:
4277     case EL_BUG_DOWN:
4278       Feld[x][y] = EL_BUG;
4279       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4280       break;
4281
4282     case EL_SPACESHIP_RIGHT:
4283     case EL_SPACESHIP_UP:
4284     case EL_SPACESHIP_LEFT:
4285     case EL_SPACESHIP_DOWN:
4286       Feld[x][y] = EL_SPACESHIP;
4287       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4288       break;
4289
4290     case EL_BD_BUTTERFLY_RIGHT:
4291     case EL_BD_BUTTERFLY_UP:
4292     case EL_BD_BUTTERFLY_LEFT:
4293     case EL_BD_BUTTERFLY_DOWN:
4294       Feld[x][y] = EL_BD_BUTTERFLY;
4295       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4296       break;
4297
4298     case EL_BD_FIREFLY_RIGHT:
4299     case EL_BD_FIREFLY_UP:
4300     case EL_BD_FIREFLY_LEFT:
4301     case EL_BD_FIREFLY_DOWN:
4302       Feld[x][y] = EL_BD_FIREFLY;
4303       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4304       break;
4305
4306     case EL_PACMAN_RIGHT:
4307     case EL_PACMAN_UP:
4308     case EL_PACMAN_LEFT:
4309     case EL_PACMAN_DOWN:
4310       Feld[x][y] = EL_PACMAN;
4311       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4312       break;
4313
4314     case EL_YAMYAM_LEFT:
4315     case EL_YAMYAM_RIGHT:
4316     case EL_YAMYAM_UP:
4317     case EL_YAMYAM_DOWN:
4318       Feld[x][y] = EL_YAMYAM;
4319       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4320       break;
4321
4322     case EL_SP_SNIKSNAK:
4323       MovDir[x][y] = MV_UP;
4324       break;
4325
4326     case EL_SP_ELECTRON:
4327       MovDir[x][y] = MV_LEFT;
4328       break;
4329
4330     case EL_MOLE_LEFT:
4331     case EL_MOLE_RIGHT:
4332     case EL_MOLE_UP:
4333     case EL_MOLE_DOWN:
4334       Feld[x][y] = EL_MOLE;
4335       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4336       break;
4337
4338     default:
4339       if (IS_CUSTOM_ELEMENT(element))
4340       {
4341         struct ElementInfo *ei = &element_info[element];
4342         int move_direction_initial = ei->move_direction_initial;
4343         int move_pattern = ei->move_pattern;
4344
4345         if (move_direction_initial == MV_START_PREVIOUS)
4346         {
4347           if (MovDir[x][y] != MV_NONE)
4348             return;
4349
4350           move_direction_initial = MV_START_AUTOMATIC;
4351         }
4352
4353         if (move_direction_initial == MV_START_RANDOM)
4354           MovDir[x][y] = 1 << RND(4);
4355         else if (move_direction_initial & MV_ANY_DIRECTION)
4356           MovDir[x][y] = move_direction_initial;
4357         else if (move_pattern == MV_ALL_DIRECTIONS ||
4358                  move_pattern == MV_TURNING_LEFT ||
4359                  move_pattern == MV_TURNING_RIGHT ||
4360                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4361                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4362                  move_pattern == MV_TURNING_RANDOM)
4363           MovDir[x][y] = 1 << RND(4);
4364         else if (move_pattern == MV_HORIZONTAL)
4365           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4366         else if (move_pattern == MV_VERTICAL)
4367           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4368         else if (move_pattern & MV_ANY_DIRECTION)
4369           MovDir[x][y] = element_info[element].move_pattern;
4370         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4371                  move_pattern == MV_ALONG_RIGHT_SIDE)
4372         {
4373           /* use random direction as default start direction */
4374           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4375             MovDir[x][y] = 1 << RND(4);
4376
4377           for (i = 0; i < NUM_DIRECTIONS; i++)
4378           {
4379             int x1 = x + xy[i][0];
4380             int y1 = y + xy[i][1];
4381
4382             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4383             {
4384               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4385                 MovDir[x][y] = direction[0][i];
4386               else
4387                 MovDir[x][y] = direction[1][i];
4388
4389               break;
4390             }
4391           }
4392         }                
4393       }
4394       else
4395       {
4396         MovDir[x][y] = 1 << RND(4);
4397
4398         if (element != EL_BUG &&
4399             element != EL_SPACESHIP &&
4400             element != EL_BD_BUTTERFLY &&
4401             element != EL_BD_FIREFLY)
4402           break;
4403
4404         for (i = 0; i < NUM_DIRECTIONS; i++)
4405         {
4406           int x1 = x + xy[i][0];
4407           int y1 = y + xy[i][1];
4408
4409           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4410           {
4411             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4412             {
4413               MovDir[x][y] = direction[0][i];
4414               break;
4415             }
4416             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4417                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4418             {
4419               MovDir[x][y] = direction[1][i];
4420               break;
4421             }
4422           }
4423         }
4424       }
4425       break;
4426   }
4427
4428   GfxDir[x][y] = MovDir[x][y];
4429 }
4430
4431 void InitAmoebaNr(int x, int y)
4432 {
4433   int i;
4434   int group_nr = AmoebeNachbarNr(x, y);
4435
4436   if (group_nr == 0)
4437   {
4438     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4439     {
4440       if (AmoebaCnt[i] == 0)
4441       {
4442         group_nr = i;
4443         break;
4444       }
4445     }
4446   }
4447
4448   AmoebaNr[x][y] = group_nr;
4449   AmoebaCnt[group_nr]++;
4450   AmoebaCnt2[group_nr]++;
4451 }
4452
4453 static void PlayerWins(struct PlayerInfo *player)
4454 {
4455   player->LevelSolved = TRUE;
4456   player->GameOver = TRUE;
4457
4458   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4459                          level.native_em_level->lev->score :
4460                          level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4461                          game_mm.score :
4462                          player->score);
4463   player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4464                           MM_HEALTH(game_mm.laser_overload_value) :
4465                           player->health);
4466
4467   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4468                                       TimeLeft);
4469   player->LevelSolved_CountingScore = player->score_final;
4470   player->LevelSolved_CountingHealth = player->health_final;
4471 }
4472
4473 void GameWon()
4474 {
4475   static int time_count_steps;
4476   static int time, time_final;
4477   static int score, score_final;
4478   static int health, health_final;
4479   static int game_over_delay_1 = 0;
4480   static int game_over_delay_2 = 0;
4481   static int game_over_delay_3 = 0;
4482   int game_over_delay_value_1 = 50;
4483   int game_over_delay_value_2 = 25;
4484   int game_over_delay_value_3 = 50;
4485
4486   if (!local_player->LevelSolved_GameWon)
4487   {
4488     int i;
4489
4490     /* do not start end game actions before the player stops moving (to exit) */
4491     if (local_player->MovPos)
4492       return;
4493
4494     local_player->LevelSolved_GameWon = TRUE;
4495     local_player->LevelSolved_SaveTape = tape.recording;
4496     local_player->LevelSolved_SaveScore = !tape.playing;
4497
4498     if (!tape.playing)
4499     {
4500       LevelStats_incSolved(level_nr);
4501
4502       SaveLevelSetup_SeriesInfo();
4503     }
4504
4505     if (tape.auto_play)         /* tape might already be stopped here */
4506       tape.auto_play_level_solved = TRUE;
4507
4508     TapeStop();
4509
4510     game_over_delay_1 = 0;
4511     game_over_delay_2 = 0;
4512     game_over_delay_3 = game_over_delay_value_3;
4513
4514     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4515     score = score_final = local_player->score_final;
4516     health = health_final = local_player->health_final;
4517
4518     if (level.score[SC_TIME_BONUS] > 0)
4519     {
4520       if (TimeLeft > 0)
4521       {
4522         time_final = 0;
4523         score_final += TimeLeft * level.score[SC_TIME_BONUS];
4524       }
4525       else if (game.no_time_limit && TimePlayed < 999)
4526       {
4527         time_final = 999;
4528         score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4529       }
4530
4531       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4532
4533       game_over_delay_1 = game_over_delay_value_1;
4534
4535       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4536       {
4537         health_final = 0;
4538         score_final += health * level.score[SC_TIME_BONUS];
4539
4540         game_over_delay_2 = game_over_delay_value_2;
4541       }
4542
4543       local_player->score_final = score_final;
4544       local_player->health_final = health_final;
4545     }
4546
4547     if (level_editor_test_game)
4548     {
4549       time = time_final;
4550       score = score_final;
4551
4552       local_player->LevelSolved_CountingTime = time;
4553       local_player->LevelSolved_CountingScore = score;
4554
4555       game_panel_controls[GAME_PANEL_TIME].value = time;
4556       game_panel_controls[GAME_PANEL_SCORE].value = score;
4557
4558       DisplayGameControlValues();
4559     }
4560
4561     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4562     {
4563       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4564       {
4565         /* close exit door after last player */
4566         if ((AllPlayersGone &&
4567              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4568               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4569               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4570             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4571             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4572         {
4573           int element = Feld[ExitX][ExitY];
4574
4575           Feld[ExitX][ExitY] =
4576             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4577              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4578              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4579              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4580              EL_EM_STEEL_EXIT_CLOSING);
4581
4582           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4583         }
4584
4585         /* player disappears */
4586         DrawLevelField(ExitX, ExitY);
4587       }
4588
4589       for (i = 0; i < MAX_PLAYERS; i++)
4590       {
4591         struct PlayerInfo *player = &stored_player[i];
4592
4593         if (player->present)
4594         {
4595           RemovePlayer(player);
4596
4597           /* player disappears */
4598           DrawLevelField(player->jx, player->jy);
4599         }
4600       }
4601     }
4602
4603     PlaySound(SND_GAME_WINNING);
4604   }
4605
4606   if (game_over_delay_1 > 0)
4607   {
4608     game_over_delay_1--;
4609
4610     return;
4611   }
4612
4613   if (time != time_final)
4614   {
4615     int time_to_go = ABS(time_final - time);
4616     int time_count_dir = (time < time_final ? +1 : -1);
4617
4618     if (time_to_go < time_count_steps)
4619       time_count_steps = 1;
4620
4621     time  += time_count_steps * time_count_dir;
4622     score += time_count_steps * level.score[SC_TIME_BONUS];
4623
4624     local_player->LevelSolved_CountingTime = time;
4625     local_player->LevelSolved_CountingScore = score;
4626
4627     game_panel_controls[GAME_PANEL_TIME].value = time;
4628     game_panel_controls[GAME_PANEL_SCORE].value = score;
4629
4630     DisplayGameControlValues();
4631
4632     if (time == time_final)
4633       StopSound(SND_GAME_LEVELTIME_BONUS);
4634     else if (setup.sound_loops)
4635       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4636     else
4637       PlaySound(SND_GAME_LEVELTIME_BONUS);
4638
4639     return;
4640   }
4641
4642   if (game_over_delay_2 > 0)
4643   {
4644     game_over_delay_2--;
4645
4646     return;
4647   }
4648
4649   if (health != health_final)
4650   {
4651     int health_count_dir = (health < health_final ? +1 : -1);
4652
4653     health += health_count_dir;
4654     score  += level.score[SC_TIME_BONUS];
4655
4656     local_player->LevelSolved_CountingHealth = health;
4657     local_player->LevelSolved_CountingScore = score;
4658
4659     game_panel_controls[GAME_PANEL_HEALTH].value = health;
4660     game_panel_controls[GAME_PANEL_SCORE].value = score;
4661
4662     DisplayGameControlValues();
4663
4664     if (health == health_final)
4665       StopSound(SND_GAME_LEVELTIME_BONUS);
4666     else if (setup.sound_loops)
4667       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4668     else
4669       PlaySound(SND_GAME_LEVELTIME_BONUS);
4670
4671     return;
4672   }
4673
4674   local_player->LevelSolved_PanelOff = TRUE;
4675
4676   if (game_over_delay_3 > 0)
4677   {
4678     game_over_delay_3--;
4679
4680     return;
4681   }
4682
4683   GameEnd();
4684 }
4685
4686 void GameEnd()
4687 {
4688   int hi_pos;
4689   boolean raise_level = FALSE;
4690
4691   local_player->LevelSolved_GameEnd = TRUE;
4692
4693   if (!global.use_envelope_request)
4694     CloseDoor(DOOR_CLOSE_1);
4695
4696   if (local_player->LevelSolved_SaveTape)
4697   {
4698     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4699   }
4700
4701   CloseDoor(DOOR_CLOSE_ALL);
4702
4703   if (level_editor_test_game)
4704   {
4705     SetGameStatus(GAME_MODE_MAIN);
4706
4707     DrawMainMenu();
4708
4709     return;
4710   }
4711
4712   if (!local_player->LevelSolved_SaveScore)
4713   {
4714     SetGameStatus(GAME_MODE_MAIN);
4715
4716     DrawMainMenu();
4717
4718     return;
4719   }
4720
4721   if (level_nr == leveldir_current->handicap_level)
4722   {
4723     leveldir_current->handicap_level++;
4724
4725     SaveLevelSetup_SeriesInfo();
4726   }
4727
4728   if (setup.increment_levels &&
4729       level_nr < leveldir_current->last_level)
4730     raise_level = TRUE;                 /* advance to next level */
4731
4732   if ((hi_pos = NewHiScore()) >= 0) 
4733   {
4734     SetGameStatus(GAME_MODE_SCORES);
4735
4736     DrawHallOfFame(hi_pos);
4737
4738     if (raise_level)
4739     {
4740       level_nr++;
4741       TapeErase();
4742     }
4743   }
4744   else
4745   {
4746     SetGameStatus(GAME_MODE_MAIN);
4747
4748     if (raise_level)
4749     {
4750       level_nr++;
4751       TapeErase();
4752     }
4753
4754     DrawMainMenu();
4755   }
4756 }
4757
4758 int NewHiScore()
4759 {
4760   int k, l;
4761   int position = -1;
4762   boolean one_score_entry_per_name = !program.many_scores_per_name;
4763
4764   LoadScore(level_nr);
4765
4766   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4767       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4768     return -1;
4769
4770   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4771   {
4772     if (local_player->score_final > highscore[k].Score)
4773     {
4774       /* player has made it to the hall of fame */
4775
4776       if (k < MAX_SCORE_ENTRIES - 1)
4777       {
4778         int m = MAX_SCORE_ENTRIES - 1;
4779
4780         if (one_score_entry_per_name)
4781         {
4782           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4783             if (strEqual(setup.player_name, highscore[l].Name))
4784               m = l;
4785
4786           if (m == k)   /* player's new highscore overwrites his old one */
4787             goto put_into_list;
4788         }
4789
4790         for (l = m; l > k; l--)
4791         {
4792           strcpy(highscore[l].Name, highscore[l - 1].Name);
4793           highscore[l].Score = highscore[l - 1].Score;
4794         }
4795       }
4796
4797       put_into_list:
4798
4799       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4800       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4801       highscore[k].Score = local_player->score_final; 
4802       position = k;
4803
4804       break;
4805     }
4806     else if (one_score_entry_per_name &&
4807              !strncmp(setup.player_name, highscore[k].Name,
4808                       MAX_PLAYER_NAME_LEN))
4809       break;    /* player already there with a higher score */
4810   }
4811
4812   if (position >= 0) 
4813     SaveScore(level_nr);
4814
4815   return position;
4816 }
4817
4818 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4819 {
4820   int element = Feld[x][y];
4821   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4822   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4823   int horiz_move = (dx != 0);
4824   int sign = (horiz_move ? dx : dy);
4825   int step = sign * element_info[element].move_stepsize;
4826
4827   /* special values for move stepsize for spring and things on conveyor belt */
4828   if (horiz_move)
4829   {
4830     if (CAN_FALL(element) &&
4831         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4832       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4833     else if (element == EL_SPRING)
4834       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4835   }
4836
4837   return step;
4838 }
4839
4840 inline static int getElementMoveStepsize(int x, int y)
4841 {
4842   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4843 }
4844
4845 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4846 {
4847   if (player->GfxAction != action || player->GfxDir != dir)
4848   {
4849     player->GfxAction = action;
4850     player->GfxDir = dir;
4851     player->Frame = 0;
4852     player->StepFrame = 0;
4853   }
4854 }
4855
4856 static void ResetGfxFrame(int x, int y)
4857 {
4858   // profiling showed that "autotest" spends 10~20% of its time in this function
4859   if (DrawingDeactivatedField())
4860     return;
4861
4862   int element = Feld[x][y];
4863   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4864
4865   if (graphic_info[graphic].anim_global_sync)
4866     GfxFrame[x][y] = FrameCounter;
4867   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4868     GfxFrame[x][y] = CustomValue[x][y];
4869   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4870     GfxFrame[x][y] = element_info[element].collect_score;
4871   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4872     GfxFrame[x][y] = ChangeDelay[x][y];
4873 }
4874
4875 static void ResetGfxAnimation(int x, int y)
4876 {
4877   GfxAction[x][y] = ACTION_DEFAULT;
4878   GfxDir[x][y] = MovDir[x][y];
4879   GfxFrame[x][y] = 0;
4880
4881   ResetGfxFrame(x, y);
4882 }
4883
4884 static void ResetRandomAnimationValue(int x, int y)
4885 {
4886   GfxRandom[x][y] = INIT_GFX_RANDOM();
4887 }
4888
4889 void InitMovingField(int x, int y, int direction)
4890 {
4891   int element = Feld[x][y];
4892   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4893   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4894   int newx = x + dx;
4895   int newy = y + dy;
4896   boolean is_moving_before, is_moving_after;
4897
4898   /* check if element was/is moving or being moved before/after mode change */
4899   is_moving_before = (WasJustMoving[x][y] != 0);
4900   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4901
4902   /* reset animation only for moving elements which change direction of moving
4903      or which just started or stopped moving
4904      (else CEs with property "can move" / "not moving" are reset each frame) */
4905   if (is_moving_before != is_moving_after ||
4906       direction != MovDir[x][y])
4907     ResetGfxAnimation(x, y);
4908
4909   MovDir[x][y] = direction;
4910   GfxDir[x][y] = direction;
4911
4912   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4913                      direction == MV_DOWN && CAN_FALL(element) ?
4914                      ACTION_FALLING : ACTION_MOVING);
4915
4916   /* this is needed for CEs with property "can move" / "not moving" */
4917
4918   if (is_moving_after)
4919   {
4920     if (Feld[newx][newy] == EL_EMPTY)
4921       Feld[newx][newy] = EL_BLOCKED;
4922
4923     MovDir[newx][newy] = MovDir[x][y];
4924
4925     CustomValue[newx][newy] = CustomValue[x][y];
4926
4927     GfxFrame[newx][newy] = GfxFrame[x][y];
4928     GfxRandom[newx][newy] = GfxRandom[x][y];
4929     GfxAction[newx][newy] = GfxAction[x][y];
4930     GfxDir[newx][newy] = GfxDir[x][y];
4931   }
4932 }
4933
4934 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4935 {
4936   int direction = MovDir[x][y];
4937   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4938   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4939
4940   *goes_to_x = newx;
4941   *goes_to_y = newy;
4942 }
4943
4944 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4945 {
4946   int oldx = x, oldy = y;
4947   int direction = MovDir[x][y];
4948
4949   if (direction == MV_LEFT)
4950     oldx++;
4951   else if (direction == MV_RIGHT)
4952     oldx--;
4953   else if (direction == MV_UP)
4954     oldy++;
4955   else if (direction == MV_DOWN)
4956     oldy--;
4957
4958   *comes_from_x = oldx;
4959   *comes_from_y = oldy;
4960 }
4961
4962 int MovingOrBlocked2Element(int x, int y)
4963 {
4964   int element = Feld[x][y];
4965
4966   if (element == EL_BLOCKED)
4967   {
4968     int oldx, oldy;
4969
4970     Blocked2Moving(x, y, &oldx, &oldy);
4971     return Feld[oldx][oldy];
4972   }
4973   else
4974     return element;
4975 }
4976
4977 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4978 {
4979   /* like MovingOrBlocked2Element(), but if element is moving
4980      and (x,y) is the field the moving element is just leaving,
4981      return EL_BLOCKED instead of the element value */
4982   int element = Feld[x][y];
4983
4984   if (IS_MOVING(x, y))
4985   {
4986     if (element == EL_BLOCKED)
4987     {
4988       int oldx, oldy;
4989
4990       Blocked2Moving(x, y, &oldx, &oldy);
4991       return Feld[oldx][oldy];
4992     }
4993     else
4994       return EL_BLOCKED;
4995   }
4996   else
4997     return element;
4998 }
4999
5000 static void RemoveField(int x, int y)
5001 {
5002   Feld[x][y] = EL_EMPTY;
5003
5004   MovPos[x][y] = 0;
5005   MovDir[x][y] = 0;
5006   MovDelay[x][y] = 0;
5007
5008   CustomValue[x][y] = 0;
5009
5010   AmoebaNr[x][y] = 0;
5011   ChangeDelay[x][y] = 0;
5012   ChangePage[x][y] = -1;
5013   Pushed[x][y] = FALSE;
5014
5015   GfxElement[x][y] = EL_UNDEFINED;
5016   GfxAction[x][y] = ACTION_DEFAULT;
5017   GfxDir[x][y] = MV_NONE;
5018 }
5019
5020 void RemoveMovingField(int x, int y)
5021 {
5022   int oldx = x, oldy = y, newx = x, newy = y;
5023   int element = Feld[x][y];
5024   int next_element = EL_UNDEFINED;
5025
5026   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5027     return;
5028
5029   if (IS_MOVING(x, y))
5030   {
5031     Moving2Blocked(x, y, &newx, &newy);
5032
5033     if (Feld[newx][newy] != EL_BLOCKED)
5034     {
5035       /* element is moving, but target field is not free (blocked), but
5036          already occupied by something different (example: acid pool);
5037          in this case, only remove the moving field, but not the target */
5038
5039       RemoveField(oldx, oldy);
5040
5041       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5042
5043       TEST_DrawLevelField(oldx, oldy);
5044
5045       return;
5046     }
5047   }
5048   else if (element == EL_BLOCKED)
5049   {
5050     Blocked2Moving(x, y, &oldx, &oldy);
5051     if (!IS_MOVING(oldx, oldy))
5052       return;
5053   }
5054
5055   if (element == EL_BLOCKED &&
5056       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5057        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5058        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5059        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5060        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5061        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5062     next_element = get_next_element(Feld[oldx][oldy]);
5063
5064   RemoveField(oldx, oldy);
5065   RemoveField(newx, newy);
5066
5067   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5068
5069   if (next_element != EL_UNDEFINED)
5070     Feld[oldx][oldy] = next_element;
5071
5072   TEST_DrawLevelField(oldx, oldy);
5073   TEST_DrawLevelField(newx, newy);
5074 }
5075
5076 void DrawDynamite(int x, int y)
5077 {
5078   int sx = SCREENX(x), sy = SCREENY(y);
5079   int graphic = el2img(Feld[x][y]);
5080   int frame;
5081
5082   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5083     return;
5084
5085   if (IS_WALKABLE_INSIDE(Back[x][y]))
5086     return;
5087
5088   if (Back[x][y])
5089     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5090   else if (Store[x][y])
5091     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5092
5093   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5094
5095   if (Back[x][y] || Store[x][y])
5096     DrawGraphicThruMask(sx, sy, graphic, frame);
5097   else
5098     DrawGraphic(sx, sy, graphic, frame);
5099 }
5100
5101 void CheckDynamite(int x, int y)
5102 {
5103   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5104   {
5105     MovDelay[x][y]--;
5106
5107     if (MovDelay[x][y] != 0)
5108     {
5109       DrawDynamite(x, y);
5110       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5111
5112       return;
5113     }
5114   }
5115
5116   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5117
5118   Bang(x, y);
5119 }
5120
5121 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5122 {
5123   boolean num_checked_players = 0;
5124   int i;
5125
5126   for (i = 0; i < MAX_PLAYERS; i++)
5127   {
5128     if (stored_player[i].active)
5129     {
5130       int sx = stored_player[i].jx;
5131       int sy = stored_player[i].jy;
5132
5133       if (num_checked_players == 0)
5134       {
5135         *sx1 = *sx2 = sx;
5136         *sy1 = *sy2 = sy;
5137       }
5138       else
5139       {
5140         *sx1 = MIN(*sx1, sx);
5141         *sy1 = MIN(*sy1, sy);
5142         *sx2 = MAX(*sx2, sx);
5143         *sy2 = MAX(*sy2, sy);
5144       }
5145
5146       num_checked_players++;
5147     }
5148   }
5149 }
5150
5151 static boolean checkIfAllPlayersFitToScreen_RND()
5152 {
5153   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5154
5155   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5156
5157   return (sx2 - sx1 < SCR_FIELDX &&
5158           sy2 - sy1 < SCR_FIELDY);
5159 }
5160
5161 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5162 {
5163   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5164
5165   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5166
5167   *sx = (sx1 + sx2) / 2;
5168   *sy = (sy1 + sy2) / 2;
5169 }
5170
5171 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5172                         boolean center_screen, boolean quick_relocation)
5173 {
5174   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5175   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5176   boolean no_delay = (tape.warp_forward);
5177   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5178   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5179   int new_scroll_x, new_scroll_y;
5180
5181   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5182   {
5183     /* case 1: quick relocation inside visible screen (without scrolling) */
5184
5185     RedrawPlayfield();
5186
5187     return;
5188   }
5189
5190   if (!level.shifted_relocation || center_screen)
5191   {
5192     /* relocation _with_ centering of screen */
5193
5194     new_scroll_x = SCROLL_POSITION_X(x);
5195     new_scroll_y = SCROLL_POSITION_Y(y);
5196   }
5197   else
5198   {
5199     /* relocation _without_ centering of screen */
5200
5201     int center_scroll_x = SCROLL_POSITION_X(old_x);
5202     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5203     int offset_x = x + (scroll_x - center_scroll_x);
5204     int offset_y = y + (scroll_y - center_scroll_y);
5205
5206     /* for new screen position, apply previous offset to center position */
5207     new_scroll_x = SCROLL_POSITION_X(offset_x);
5208     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5209   }
5210
5211   if (quick_relocation)
5212   {
5213     /* case 2: quick relocation (redraw without visible scrolling) */
5214
5215     scroll_x = new_scroll_x;
5216     scroll_y = new_scroll_y;
5217
5218     RedrawPlayfield();
5219
5220     return;
5221   }
5222
5223   /* case 3: visible relocation (with scrolling to new position) */
5224
5225   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
5226
5227   SetVideoFrameDelay(wait_delay_value);
5228
5229   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5230   {
5231     int dx = 0, dy = 0;
5232     int fx = FX, fy = FY;
5233
5234     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5235     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5236
5237     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
5238       break;
5239
5240     scroll_x -= dx;
5241     scroll_y -= dy;
5242
5243     fx += dx * TILEX / 2;
5244     fy += dy * TILEY / 2;
5245
5246     ScrollLevel(dx, dy);
5247     DrawAllPlayers();
5248
5249     /* scroll in two steps of half tile size to make things smoother */
5250     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5251
5252     /* scroll second step to align at full tile size */
5253     BlitScreenToBitmap(window);
5254   }
5255
5256   DrawAllPlayers();
5257   BackToFront();
5258
5259   SetVideoFrameDelay(frame_delay_value_old);
5260 }
5261
5262 void RelocatePlayer(int jx, int jy, int el_player_raw)
5263 {
5264   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5265   int player_nr = GET_PLAYER_NR(el_player);
5266   struct PlayerInfo *player = &stored_player[player_nr];
5267   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5268   boolean no_delay = (tape.warp_forward);
5269   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5270   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5271   int old_jx = player->jx;
5272   int old_jy = player->jy;
5273   int old_element = Feld[old_jx][old_jy];
5274   int element = Feld[jx][jy];
5275   boolean player_relocated = (old_jx != jx || old_jy != jy);
5276
5277   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5278   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5279   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5280   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5281   int leave_side_horiz = move_dir_horiz;
5282   int leave_side_vert  = move_dir_vert;
5283   int enter_side = enter_side_horiz | enter_side_vert;
5284   int leave_side = leave_side_horiz | leave_side_vert;
5285
5286   if (player->GameOver)         /* do not reanimate dead player */
5287     return;
5288
5289   if (!player_relocated)        /* no need to relocate the player */
5290     return;
5291
5292   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5293   {
5294     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5295     DrawLevelField(jx, jy);
5296   }
5297
5298   if (player->present)
5299   {
5300     while (player->MovPos)
5301     {
5302       ScrollPlayer(player, SCROLL_GO_ON);
5303       ScrollScreen(NULL, SCROLL_GO_ON);
5304
5305       AdvanceFrameAndPlayerCounters(player->index_nr);
5306
5307       DrawPlayer(player);
5308
5309       BackToFront_WithFrameDelay(wait_delay_value);
5310     }
5311
5312     DrawPlayer(player);         /* needed here only to cleanup last field */
5313     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5314
5315     player->is_moving = FALSE;
5316   }
5317
5318   if (IS_CUSTOM_ELEMENT(old_element))
5319     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5320                                CE_LEFT_BY_PLAYER,
5321                                player->index_bit, leave_side);
5322
5323   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5324                                       CE_PLAYER_LEAVES_X,
5325                                       player->index_bit, leave_side);
5326
5327   Feld[jx][jy] = el_player;
5328   InitPlayerField(jx, jy, el_player, TRUE);
5329
5330   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5331      possible that the relocation target field did not contain a player element,
5332      but a walkable element, to which the new player was relocated -- in this
5333      case, restore that (already initialized!) element on the player field */
5334   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5335   {
5336     Feld[jx][jy] = element;     /* restore previously existing element */
5337   }
5338
5339   /* only visually relocate centered player */
5340   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5341                      FALSE, level.instant_relocation);
5342
5343   TestIfPlayerTouchesBadThing(jx, jy);
5344   TestIfPlayerTouchesCustomElement(jx, jy);
5345
5346   if (IS_CUSTOM_ELEMENT(element))
5347     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5348                                player->index_bit, enter_side);
5349
5350   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5351                                       player->index_bit, enter_side);
5352
5353   if (player->is_switching)
5354   {
5355     /* ensure that relocation while still switching an element does not cause
5356        a new element to be treated as also switched directly after relocation
5357        (this is important for teleporter switches that teleport the player to
5358        a place where another teleporter switch is in the same direction, which
5359        would then incorrectly be treated as immediately switched before the
5360        direction key that caused the switch was released) */
5361
5362     player->switch_x += jx - old_jx;
5363     player->switch_y += jy - old_jy;
5364   }
5365 }
5366
5367 void Explode(int ex, int ey, int phase, int mode)
5368 {
5369   int x, y;
5370   int last_phase;
5371   int border_element;
5372
5373   /* !!! eliminate this variable !!! */
5374   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5375
5376   if (game.explosions_delayed)
5377   {
5378     ExplodeField[ex][ey] = mode;
5379     return;
5380   }
5381
5382   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5383   {
5384     int center_element = Feld[ex][ey];
5385     int artwork_element, explosion_element;     /* set these values later */
5386
5387     /* remove things displayed in background while burning dynamite */
5388     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5389       Back[ex][ey] = 0;
5390
5391     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5392     {
5393       /* put moving element to center field (and let it explode there) */
5394       center_element = MovingOrBlocked2Element(ex, ey);
5395       RemoveMovingField(ex, ey);
5396       Feld[ex][ey] = center_element;
5397     }
5398
5399     /* now "center_element" is finally determined -- set related values now */
5400     artwork_element = center_element;           /* for custom player artwork */
5401     explosion_element = center_element;         /* for custom player artwork */
5402
5403     if (IS_PLAYER(ex, ey))
5404     {
5405       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5406
5407       artwork_element = stored_player[player_nr].artwork_element;
5408
5409       if (level.use_explosion_element[player_nr])
5410       {
5411         explosion_element = level.explosion_element[player_nr];
5412         artwork_element = explosion_element;
5413       }
5414     }
5415
5416     if (mode == EX_TYPE_NORMAL ||
5417         mode == EX_TYPE_CENTER ||
5418         mode == EX_TYPE_CROSS)
5419       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5420
5421     last_phase = element_info[explosion_element].explosion_delay + 1;
5422
5423     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5424     {
5425       int xx = x - ex + 1;
5426       int yy = y - ey + 1;
5427       int element;
5428
5429       if (!IN_LEV_FIELD(x, y) ||
5430           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5431           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5432         continue;
5433
5434       element = Feld[x][y];
5435
5436       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5437       {
5438         element = MovingOrBlocked2Element(x, y);
5439
5440         if (!IS_EXPLOSION_PROOF(element))
5441           RemoveMovingField(x, y);
5442       }
5443
5444       /* indestructible elements can only explode in center (but not flames) */
5445       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5446                                            mode == EX_TYPE_BORDER)) ||
5447           element == EL_FLAMES)
5448         continue;
5449
5450       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5451          behaviour, for example when touching a yamyam that explodes to rocks
5452          with active deadly shield, a rock is created under the player !!! */
5453       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5454 #if 0
5455       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5456           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5457            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5458 #else
5459       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5460 #endif
5461       {
5462         if (IS_ACTIVE_BOMB(element))
5463         {
5464           /* re-activate things under the bomb like gate or penguin */
5465           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5466           Back[x][y] = 0;
5467         }
5468
5469         continue;
5470       }
5471
5472       /* save walkable background elements while explosion on same tile */
5473       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5474           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5475         Back[x][y] = element;
5476
5477       /* ignite explodable elements reached by other explosion */
5478       if (element == EL_EXPLOSION)
5479         element = Store2[x][y];
5480
5481       if (AmoebaNr[x][y] &&
5482           (element == EL_AMOEBA_FULL ||
5483            element == EL_BD_AMOEBA ||
5484            element == EL_AMOEBA_GROWING))
5485       {
5486         AmoebaCnt[AmoebaNr[x][y]]--;
5487         AmoebaCnt2[AmoebaNr[x][y]]--;
5488       }
5489
5490       RemoveField(x, y);
5491
5492       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5493       {
5494         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5495
5496         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5497
5498         if (PLAYERINFO(ex, ey)->use_murphy)
5499           Store[x][y] = EL_EMPTY;
5500       }
5501
5502       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5503          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5504       else if (ELEM_IS_PLAYER(center_element))
5505         Store[x][y] = EL_EMPTY;
5506       else if (center_element == EL_YAMYAM)
5507         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5508       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5509         Store[x][y] = element_info[center_element].content.e[xx][yy];
5510 #if 1
5511       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5512          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5513          otherwise) -- FIX THIS !!! */
5514       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5515         Store[x][y] = element_info[element].content.e[1][1];
5516 #else
5517       else if (!CAN_EXPLODE(element))
5518         Store[x][y] = element_info[element].content.e[1][1];
5519 #endif
5520       else
5521         Store[x][y] = EL_EMPTY;
5522
5523       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5524           center_element == EL_AMOEBA_TO_DIAMOND)
5525         Store2[x][y] = element;
5526
5527       Feld[x][y] = EL_EXPLOSION;
5528       GfxElement[x][y] = artwork_element;
5529
5530       ExplodePhase[x][y] = 1;
5531       ExplodeDelay[x][y] = last_phase;
5532
5533       Stop[x][y] = TRUE;
5534     }
5535
5536     if (center_element == EL_YAMYAM)
5537       game.yamyam_content_nr =
5538         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5539
5540     return;
5541   }
5542
5543   if (Stop[ex][ey])
5544     return;
5545
5546   x = ex;
5547   y = ey;
5548
5549   if (phase == 1)
5550     GfxFrame[x][y] = 0;         /* restart explosion animation */
5551
5552   last_phase = ExplodeDelay[x][y];
5553
5554   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5555
5556   /* this can happen if the player leaves an explosion just in time */
5557   if (GfxElement[x][y] == EL_UNDEFINED)
5558     GfxElement[x][y] = EL_EMPTY;
5559
5560   border_element = Store2[x][y];
5561   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5562     border_element = StorePlayer[x][y];
5563
5564   if (phase == element_info[border_element].ignition_delay ||
5565       phase == last_phase)
5566   {
5567     boolean border_explosion = FALSE;
5568
5569     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5570         !PLAYER_EXPLOSION_PROTECTED(x, y))
5571     {
5572       KillPlayerUnlessExplosionProtected(x, y);
5573       border_explosion = TRUE;
5574     }
5575     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5576     {
5577       Feld[x][y] = Store2[x][y];
5578       Store2[x][y] = 0;
5579       Bang(x, y);
5580       border_explosion = TRUE;
5581     }
5582     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5583     {
5584       AmoebeUmwandeln(x, y);
5585       Store2[x][y] = 0;
5586       border_explosion = TRUE;
5587     }
5588
5589     /* if an element just explodes due to another explosion (chain-reaction),
5590        do not immediately end the new explosion when it was the last frame of
5591        the explosion (as it would be done in the following "if"-statement!) */
5592     if (border_explosion && phase == last_phase)
5593       return;
5594   }
5595
5596   if (phase == last_phase)
5597   {
5598     int element;
5599
5600     element = Feld[x][y] = Store[x][y];
5601     Store[x][y] = Store2[x][y] = 0;
5602     GfxElement[x][y] = EL_UNDEFINED;
5603
5604     /* player can escape from explosions and might therefore be still alive */
5605     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5606         element <= EL_PLAYER_IS_EXPLODING_4)
5607     {
5608       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5609       int explosion_element = EL_PLAYER_1 + player_nr;
5610       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5611       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5612
5613       if (level.use_explosion_element[player_nr])
5614         explosion_element = level.explosion_element[player_nr];
5615
5616       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5617                     element_info[explosion_element].content.e[xx][yy]);
5618     }
5619
5620     /* restore probably existing indestructible background element */
5621     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5622       element = Feld[x][y] = Back[x][y];
5623     Back[x][y] = 0;
5624
5625     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5626     GfxDir[x][y] = MV_NONE;
5627     ChangeDelay[x][y] = 0;
5628     ChangePage[x][y] = -1;
5629
5630     CustomValue[x][y] = 0;
5631
5632     InitField_WithBug2(x, y, FALSE);
5633
5634     TEST_DrawLevelField(x, y);
5635
5636     TestIfElementTouchesCustomElement(x, y);
5637
5638     if (GFX_CRUMBLED(element))
5639       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5640
5641     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5642       StorePlayer[x][y] = 0;
5643
5644     if (ELEM_IS_PLAYER(element))
5645       RelocatePlayer(x, y, element);
5646   }
5647   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5648   {
5649     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5650     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5651
5652     if (phase == delay)
5653       TEST_DrawLevelFieldCrumbled(x, y);
5654
5655     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5656     {
5657       DrawLevelElement(x, y, Back[x][y]);
5658       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5659     }
5660     else if (IS_WALKABLE_UNDER(Back[x][y]))
5661     {
5662       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5663       DrawLevelElementThruMask(x, y, Back[x][y]);
5664     }
5665     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5666       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5667   }
5668 }
5669
5670 void DynaExplode(int ex, int ey)
5671 {
5672   int i, j;
5673   int dynabomb_element = Feld[ex][ey];
5674   int dynabomb_size = 1;
5675   boolean dynabomb_xl = FALSE;
5676   struct PlayerInfo *player;
5677   static int xy[4][2] =
5678   {
5679     { 0, -1 },
5680     { -1, 0 },
5681     { +1, 0 },
5682     { 0, +1 }
5683   };
5684
5685   if (IS_ACTIVE_BOMB(dynabomb_element))
5686   {
5687     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5688     dynabomb_size = player->dynabomb_size;
5689     dynabomb_xl = player->dynabomb_xl;
5690     player->dynabombs_left++;
5691   }
5692
5693   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5694
5695   for (i = 0; i < NUM_DIRECTIONS; i++)
5696   {
5697     for (j = 1; j <= dynabomb_size; j++)
5698     {
5699       int x = ex + j * xy[i][0];
5700       int y = ey + j * xy[i][1];
5701       int element;
5702
5703       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5704         break;
5705
5706       element = Feld[x][y];
5707
5708       /* do not restart explosions of fields with active bombs */
5709       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5710         continue;
5711
5712       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5713
5714       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5715           !IS_DIGGABLE(element) && !dynabomb_xl)
5716         break;
5717     }
5718   }
5719 }
5720
5721 void Bang(int x, int y)
5722 {
5723   int element = MovingOrBlocked2Element(x, y);
5724   int explosion_type = EX_TYPE_NORMAL;
5725
5726   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5727   {
5728     struct PlayerInfo *player = PLAYERINFO(x, y);
5729
5730     element = Feld[x][y] = player->initial_element;
5731
5732     if (level.use_explosion_element[player->index_nr])
5733     {
5734       int explosion_element = level.explosion_element[player->index_nr];
5735
5736       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5737         explosion_type = EX_TYPE_CROSS;
5738       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5739         explosion_type = EX_TYPE_CENTER;
5740     }
5741   }
5742
5743   switch (element)
5744   {
5745     case EL_BUG:
5746     case EL_SPACESHIP:
5747     case EL_BD_BUTTERFLY:
5748     case EL_BD_FIREFLY:
5749     case EL_YAMYAM:
5750     case EL_DARK_YAMYAM:
5751     case EL_ROBOT:
5752     case EL_PACMAN:
5753     case EL_MOLE:
5754       RaiseScoreElement(element);
5755       break;
5756
5757     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5758     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5759     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5760     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5761     case EL_DYNABOMB_INCREASE_NUMBER:
5762     case EL_DYNABOMB_INCREASE_SIZE:
5763     case EL_DYNABOMB_INCREASE_POWER:
5764       explosion_type = EX_TYPE_DYNA;
5765       break;
5766
5767     case EL_DC_LANDMINE:
5768       explosion_type = EX_TYPE_CENTER;
5769       break;
5770
5771     case EL_PENGUIN:
5772     case EL_LAMP:
5773     case EL_LAMP_ACTIVE:
5774     case EL_AMOEBA_TO_DIAMOND:
5775       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5776         explosion_type = EX_TYPE_CENTER;
5777       break;
5778
5779     default:
5780       if (element_info[element].explosion_type == EXPLODES_CROSS)
5781         explosion_type = EX_TYPE_CROSS;
5782       else if (element_info[element].explosion_type == EXPLODES_1X1)
5783         explosion_type = EX_TYPE_CENTER;
5784       break;
5785   }
5786
5787   if (explosion_type == EX_TYPE_DYNA)
5788     DynaExplode(x, y);
5789   else
5790     Explode(x, y, EX_PHASE_START, explosion_type);
5791
5792   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5793 }
5794
5795 void SplashAcid(int x, int y)
5796 {
5797   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5798       (!IN_LEV_FIELD(x - 1, y - 2) ||
5799        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5800     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5801
5802   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5803       (!IN_LEV_FIELD(x + 1, y - 2) ||
5804        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5805     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5806
5807   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5808 }
5809
5810 static void InitBeltMovement()
5811 {
5812   static int belt_base_element[4] =
5813   {
5814     EL_CONVEYOR_BELT_1_LEFT,
5815     EL_CONVEYOR_BELT_2_LEFT,
5816     EL_CONVEYOR_BELT_3_LEFT,
5817     EL_CONVEYOR_BELT_4_LEFT
5818   };
5819   static int belt_base_active_element[4] =
5820   {
5821     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5822     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5823     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5824     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5825   };
5826
5827   int x, y, i, j;
5828
5829   /* set frame order for belt animation graphic according to belt direction */
5830   for (i = 0; i < NUM_BELTS; i++)
5831   {
5832     int belt_nr = i;
5833
5834     for (j = 0; j < NUM_BELT_PARTS; j++)
5835     {
5836       int element = belt_base_active_element[belt_nr] + j;
5837       int graphic_1 = el2img(element);
5838       int graphic_2 = el2panelimg(element);
5839
5840       if (game.belt_dir[i] == MV_LEFT)
5841       {
5842         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5843         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5844       }
5845       else
5846       {
5847         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5848         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5849       }
5850     }
5851   }
5852
5853   SCAN_PLAYFIELD(x, y)
5854   {
5855     int element = Feld[x][y];
5856
5857     for (i = 0; i < NUM_BELTS; i++)
5858     {
5859       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5860       {
5861         int e_belt_nr = getBeltNrFromBeltElement(element);
5862         int belt_nr = i;
5863
5864         if (e_belt_nr == belt_nr)
5865         {
5866           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5867
5868           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5869         }
5870       }
5871     }
5872   }
5873 }
5874
5875 static void ToggleBeltSwitch(int x, int y)
5876 {
5877   static int belt_base_element[4] =
5878   {
5879     EL_CONVEYOR_BELT_1_LEFT,
5880     EL_CONVEYOR_BELT_2_LEFT,
5881     EL_CONVEYOR_BELT_3_LEFT,
5882     EL_CONVEYOR_BELT_4_LEFT
5883   };
5884   static int belt_base_active_element[4] =
5885   {
5886     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5887     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5888     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5889     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5890   };
5891   static int belt_base_switch_element[4] =
5892   {
5893     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5894     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5895     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5896     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5897   };
5898   static int belt_move_dir[4] =
5899   {
5900     MV_LEFT,
5901     MV_NONE,
5902     MV_RIGHT,
5903     MV_NONE,
5904   };
5905
5906   int element = Feld[x][y];
5907   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5908   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5909   int belt_dir = belt_move_dir[belt_dir_nr];
5910   int xx, yy, i;
5911
5912   if (!IS_BELT_SWITCH(element))
5913     return;
5914
5915   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5916   game.belt_dir[belt_nr] = belt_dir;
5917
5918   if (belt_dir_nr == 3)
5919     belt_dir_nr = 1;
5920
5921   /* set frame order for belt animation graphic according to belt direction */
5922   for (i = 0; i < NUM_BELT_PARTS; i++)
5923   {
5924     int element = belt_base_active_element[belt_nr] + i;
5925     int graphic_1 = el2img(element);
5926     int graphic_2 = el2panelimg(element);
5927
5928     if (belt_dir == MV_LEFT)
5929     {
5930       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5931       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5932     }
5933     else
5934     {
5935       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5936       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5937     }
5938   }
5939
5940   SCAN_PLAYFIELD(xx, yy)
5941   {
5942     int element = Feld[xx][yy];
5943
5944     if (IS_BELT_SWITCH(element))
5945     {
5946       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5947
5948       if (e_belt_nr == belt_nr)
5949       {
5950         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5951         TEST_DrawLevelField(xx, yy);
5952       }
5953     }
5954     else if (IS_BELT(element) && belt_dir != MV_NONE)
5955     {
5956       int e_belt_nr = getBeltNrFromBeltElement(element);
5957
5958       if (e_belt_nr == belt_nr)
5959       {
5960         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5961
5962         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5963         TEST_DrawLevelField(xx, yy);
5964       }
5965     }
5966     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5967     {
5968       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5969
5970       if (e_belt_nr == belt_nr)
5971       {
5972         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5973
5974         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5975         TEST_DrawLevelField(xx, yy);
5976       }
5977     }
5978   }
5979 }
5980
5981 static void ToggleSwitchgateSwitch(int x, int y)
5982 {
5983   int xx, yy;
5984
5985   game.switchgate_pos = !game.switchgate_pos;
5986
5987   SCAN_PLAYFIELD(xx, yy)
5988   {
5989     int element = Feld[xx][yy];
5990
5991     if (element == EL_SWITCHGATE_SWITCH_UP)
5992     {
5993       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5994       TEST_DrawLevelField(xx, yy);
5995     }
5996     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5997     {
5998       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5999       TEST_DrawLevelField(xx, yy);
6000     }
6001     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6002     {
6003       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6004       TEST_DrawLevelField(xx, yy);
6005     }
6006     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6007     {
6008       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6009       TEST_DrawLevelField(xx, yy);
6010     }
6011     else if (element == EL_SWITCHGATE_OPEN ||
6012              element == EL_SWITCHGATE_OPENING)
6013     {
6014       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6015
6016       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6017     }
6018     else if (element == EL_SWITCHGATE_CLOSED ||
6019              element == EL_SWITCHGATE_CLOSING)
6020     {
6021       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6022
6023       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6024     }
6025   }
6026 }
6027
6028 static int getInvisibleActiveFromInvisibleElement(int element)
6029 {
6030   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6031           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6032           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6033           element);
6034 }
6035
6036 static int getInvisibleFromInvisibleActiveElement(int element)
6037 {
6038   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6039           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6040           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6041           element);
6042 }
6043
6044 static void RedrawAllLightSwitchesAndInvisibleElements()
6045 {
6046   int x, y;
6047
6048   SCAN_PLAYFIELD(x, y)
6049   {
6050     int element = Feld[x][y];
6051
6052     if (element == EL_LIGHT_SWITCH &&
6053         game.light_time_left > 0)
6054     {
6055       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6056       TEST_DrawLevelField(x, y);
6057     }
6058     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6059              game.light_time_left == 0)
6060     {
6061       Feld[x][y] = EL_LIGHT_SWITCH;
6062       TEST_DrawLevelField(x, y);
6063     }
6064     else if (element == EL_EMC_DRIPPER &&
6065              game.light_time_left > 0)
6066     {
6067       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6068       TEST_DrawLevelField(x, y);
6069     }
6070     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6071              game.light_time_left == 0)
6072     {
6073       Feld[x][y] = EL_EMC_DRIPPER;
6074       TEST_DrawLevelField(x, y);
6075     }
6076     else if (element == EL_INVISIBLE_STEELWALL ||
6077              element == EL_INVISIBLE_WALL ||
6078              element == EL_INVISIBLE_SAND)
6079     {
6080       if (game.light_time_left > 0)
6081         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6082
6083       TEST_DrawLevelField(x, y);
6084
6085       /* uncrumble neighbour fields, if needed */
6086       if (element == EL_INVISIBLE_SAND)
6087         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6088     }
6089     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6090              element == EL_INVISIBLE_WALL_ACTIVE ||
6091              element == EL_INVISIBLE_SAND_ACTIVE)
6092     {
6093       if (game.light_time_left == 0)
6094         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6095
6096       TEST_DrawLevelField(x, y);
6097
6098       /* re-crumble neighbour fields, if needed */
6099       if (element == EL_INVISIBLE_SAND)
6100         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6101     }
6102   }
6103 }
6104
6105 static void RedrawAllInvisibleElementsForLenses()
6106 {
6107   int x, y;
6108
6109   SCAN_PLAYFIELD(x, y)
6110   {
6111     int element = Feld[x][y];
6112
6113     if (element == EL_EMC_DRIPPER &&
6114         game.lenses_time_left > 0)
6115     {
6116       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6117       TEST_DrawLevelField(x, y);
6118     }
6119     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6120              game.lenses_time_left == 0)
6121     {
6122       Feld[x][y] = EL_EMC_DRIPPER;
6123       TEST_DrawLevelField(x, y);
6124     }
6125     else if (element == EL_INVISIBLE_STEELWALL ||
6126              element == EL_INVISIBLE_WALL ||
6127              element == EL_INVISIBLE_SAND)
6128     {
6129       if (game.lenses_time_left > 0)
6130         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6131
6132       TEST_DrawLevelField(x, y);
6133
6134       /* uncrumble neighbour fields, if needed */
6135       if (element == EL_INVISIBLE_SAND)
6136         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6137     }
6138     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6139              element == EL_INVISIBLE_WALL_ACTIVE ||
6140              element == EL_INVISIBLE_SAND_ACTIVE)
6141     {
6142       if (game.lenses_time_left == 0)
6143         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6144
6145       TEST_DrawLevelField(x, y);
6146
6147       /* re-crumble neighbour fields, if needed */
6148       if (element == EL_INVISIBLE_SAND)
6149         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6150     }
6151   }
6152 }
6153
6154 static void RedrawAllInvisibleElementsForMagnifier()
6155 {
6156   int x, y;
6157
6158   SCAN_PLAYFIELD(x, y)
6159   {
6160     int element = Feld[x][y];
6161
6162     if (element == EL_EMC_FAKE_GRASS &&
6163         game.magnify_time_left > 0)
6164     {
6165       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6166       TEST_DrawLevelField(x, y);
6167     }
6168     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6169              game.magnify_time_left == 0)
6170     {
6171       Feld[x][y] = EL_EMC_FAKE_GRASS;
6172       TEST_DrawLevelField(x, y);
6173     }
6174     else if (IS_GATE_GRAY(element) &&
6175              game.magnify_time_left > 0)
6176     {
6177       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6178                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6179                     IS_EM_GATE_GRAY(element) ?
6180                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6181                     IS_EMC_GATE_GRAY(element) ?
6182                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6183                     IS_DC_GATE_GRAY(element) ?
6184                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6185                     element);
6186       TEST_DrawLevelField(x, y);
6187     }
6188     else if (IS_GATE_GRAY_ACTIVE(element) &&
6189              game.magnify_time_left == 0)
6190     {
6191       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6192                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6193                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6194                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6195                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6196                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6197                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6198                     EL_DC_GATE_WHITE_GRAY :
6199                     element);
6200       TEST_DrawLevelField(x, y);
6201     }
6202   }
6203 }
6204
6205 static void ToggleLightSwitch(int x, int y)
6206 {
6207   int element = Feld[x][y];
6208
6209   game.light_time_left =
6210     (element == EL_LIGHT_SWITCH ?
6211      level.time_light * FRAMES_PER_SECOND : 0);
6212
6213   RedrawAllLightSwitchesAndInvisibleElements();
6214 }
6215
6216 static void ActivateTimegateSwitch(int x, int y)
6217 {
6218   int xx, yy;
6219
6220   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6221
6222   SCAN_PLAYFIELD(xx, yy)
6223   {
6224     int element = Feld[xx][yy];
6225
6226     if (element == EL_TIMEGATE_CLOSED ||
6227         element == EL_TIMEGATE_CLOSING)
6228     {
6229       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6230       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6231     }
6232
6233     /*
6234     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6235     {
6236       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6237       TEST_DrawLevelField(xx, yy);
6238     }
6239     */
6240
6241   }
6242
6243   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6244                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6245 }
6246
6247 void Impact(int x, int y)
6248 {
6249   boolean last_line = (y == lev_fieldy - 1);
6250   boolean object_hit = FALSE;
6251   boolean impact = (last_line || object_hit);
6252   int element = Feld[x][y];
6253   int smashed = EL_STEELWALL;
6254
6255   if (!last_line)       /* check if element below was hit */
6256   {
6257     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6258       return;
6259
6260     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6261                                          MovDir[x][y + 1] != MV_DOWN ||
6262                                          MovPos[x][y + 1] <= TILEY / 2));
6263
6264     /* do not smash moving elements that left the smashed field in time */
6265     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6266         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6267       object_hit = FALSE;
6268
6269 #if USE_QUICKSAND_IMPACT_BUGFIX
6270     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6271     {
6272       RemoveMovingField(x, y + 1);
6273       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6274       Feld[x][y + 2] = EL_ROCK;
6275       TEST_DrawLevelField(x, y + 2);
6276
6277       object_hit = TRUE;
6278     }
6279
6280     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6281     {
6282       RemoveMovingField(x, y + 1);
6283       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6284       Feld[x][y + 2] = EL_ROCK;
6285       TEST_DrawLevelField(x, y + 2);
6286
6287       object_hit = TRUE;
6288     }
6289 #endif
6290
6291     if (object_hit)
6292       smashed = MovingOrBlocked2Element(x, y + 1);
6293
6294     impact = (last_line || object_hit);
6295   }
6296
6297   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6298   {
6299     SplashAcid(x, y + 1);
6300     return;
6301   }
6302
6303   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6304   /* only reset graphic animation if graphic really changes after impact */
6305   if (impact &&
6306       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6307   {
6308     ResetGfxAnimation(x, y);
6309     TEST_DrawLevelField(x, y);
6310   }
6311
6312   if (impact && CAN_EXPLODE_IMPACT(element))
6313   {
6314     Bang(x, y);
6315     return;
6316   }
6317   else if (impact && element == EL_PEARL &&
6318            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6319   {
6320     ResetGfxAnimation(x, y);
6321
6322     Feld[x][y] = EL_PEARL_BREAKING;
6323     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6324     return;
6325   }
6326   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6327   {
6328     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6329
6330     return;
6331   }
6332
6333   if (impact && element == EL_AMOEBA_DROP)
6334   {
6335     if (object_hit && IS_PLAYER(x, y + 1))
6336       KillPlayerUnlessEnemyProtected(x, y + 1);
6337     else if (object_hit && smashed == EL_PENGUIN)
6338       Bang(x, y + 1);
6339     else
6340     {
6341       Feld[x][y] = EL_AMOEBA_GROWING;
6342       Store[x][y] = EL_AMOEBA_WET;
6343
6344       ResetRandomAnimationValue(x, y);
6345     }
6346     return;
6347   }
6348
6349   if (object_hit)               /* check which object was hit */
6350   {
6351     if ((CAN_PASS_MAGIC_WALL(element) && 
6352          (smashed == EL_MAGIC_WALL ||
6353           smashed == EL_BD_MAGIC_WALL)) ||
6354         (CAN_PASS_DC_MAGIC_WALL(element) &&
6355          smashed == EL_DC_MAGIC_WALL))
6356     {
6357       int xx, yy;
6358       int activated_magic_wall =
6359         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6360          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6361          EL_DC_MAGIC_WALL_ACTIVE);
6362
6363       /* activate magic wall / mill */
6364       SCAN_PLAYFIELD(xx, yy)
6365       {
6366         if (Feld[xx][yy] == smashed)
6367           Feld[xx][yy] = activated_magic_wall;
6368       }
6369
6370       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6371       game.magic_wall_active = TRUE;
6372
6373       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6374                             SND_MAGIC_WALL_ACTIVATING :
6375                             smashed == EL_BD_MAGIC_WALL ?
6376                             SND_BD_MAGIC_WALL_ACTIVATING :
6377                             SND_DC_MAGIC_WALL_ACTIVATING));
6378     }
6379
6380     if (IS_PLAYER(x, y + 1))
6381     {
6382       if (CAN_SMASH_PLAYER(element))
6383       {
6384         KillPlayerUnlessEnemyProtected(x, y + 1);
6385         return;
6386       }
6387     }
6388     else if (smashed == EL_PENGUIN)
6389     {
6390       if (CAN_SMASH_PLAYER(element))
6391       {
6392         Bang(x, y + 1);
6393         return;
6394       }
6395     }
6396     else if (element == EL_BD_DIAMOND)
6397     {
6398       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6399       {
6400         Bang(x, y + 1);
6401         return;
6402       }
6403     }
6404     else if (((element == EL_SP_INFOTRON ||
6405                element == EL_SP_ZONK) &&
6406               (smashed == EL_SP_SNIKSNAK ||
6407                smashed == EL_SP_ELECTRON ||
6408                smashed == EL_SP_DISK_ORANGE)) ||
6409              (element == EL_SP_INFOTRON &&
6410               smashed == EL_SP_DISK_YELLOW))
6411     {
6412       Bang(x, y + 1);
6413       return;
6414     }
6415     else if (CAN_SMASH_EVERYTHING(element))
6416     {
6417       if (IS_CLASSIC_ENEMY(smashed) ||
6418           CAN_EXPLODE_SMASHED(smashed))
6419       {
6420         Bang(x, y + 1);
6421         return;
6422       }
6423       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6424       {
6425         if (smashed == EL_LAMP ||
6426             smashed == EL_LAMP_ACTIVE)
6427         {
6428           Bang(x, y + 1);
6429           return;
6430         }
6431         else if (smashed == EL_NUT)
6432         {
6433           Feld[x][y + 1] = EL_NUT_BREAKING;
6434           PlayLevelSound(x, y, SND_NUT_BREAKING);
6435           RaiseScoreElement(EL_NUT);
6436           return;
6437         }
6438         else if (smashed == EL_PEARL)
6439         {
6440           ResetGfxAnimation(x, y);
6441
6442           Feld[x][y + 1] = EL_PEARL_BREAKING;
6443           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6444           return;
6445         }
6446         else if (smashed == EL_DIAMOND)
6447         {
6448           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6449           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6450           return;
6451         }
6452         else if (IS_BELT_SWITCH(smashed))
6453         {
6454           ToggleBeltSwitch(x, y + 1);
6455         }
6456         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6457                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6458                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6459                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6460         {
6461           ToggleSwitchgateSwitch(x, y + 1);
6462         }
6463         else if (smashed == EL_LIGHT_SWITCH ||
6464                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6465         {
6466           ToggleLightSwitch(x, y + 1);
6467         }
6468         else
6469         {
6470           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6471
6472           CheckElementChangeBySide(x, y + 1, smashed, element,
6473                                    CE_SWITCHED, CH_SIDE_TOP);
6474           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6475                                             CH_SIDE_TOP);
6476         }
6477       }
6478       else
6479       {
6480         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6481       }
6482     }
6483   }
6484
6485   /* play sound of magic wall / mill */
6486   if (!last_line &&
6487       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6488        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6489        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6490   {
6491     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6492       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6493     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6494       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6495     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6496       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6497
6498     return;
6499   }
6500
6501   /* play sound of object that hits the ground */
6502   if (last_line || object_hit)
6503     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6504 }
6505
6506 inline static void TurnRoundExt(int x, int y)
6507 {
6508   static struct
6509   {
6510     int dx, dy;
6511   } move_xy[] =
6512   {
6513     {  0,  0 },
6514     { -1,  0 },
6515     { +1,  0 },
6516     {  0,  0 },
6517     {  0, -1 },
6518     {  0,  0 }, { 0, 0 }, { 0, 0 },
6519     {  0, +1 }
6520   };
6521   static struct
6522   {
6523     int left, right, back;
6524   } turn[] =
6525   {
6526     { 0,        0,              0        },
6527     { MV_DOWN,  MV_UP,          MV_RIGHT },
6528     { MV_UP,    MV_DOWN,        MV_LEFT  },
6529     { 0,        0,              0        },
6530     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6531     { 0,        0,              0        },
6532     { 0,        0,              0        },
6533     { 0,        0,              0        },
6534     { MV_RIGHT, MV_LEFT,        MV_UP    }
6535   };
6536
6537   int element = Feld[x][y];
6538   int move_pattern = element_info[element].move_pattern;
6539
6540   int old_move_dir = MovDir[x][y];
6541   int left_dir  = turn[old_move_dir].left;
6542   int right_dir = turn[old_move_dir].right;
6543   int back_dir  = turn[old_move_dir].back;
6544
6545   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6546   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6547   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6548   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6549
6550   int left_x  = x + left_dx,  left_y  = y + left_dy;
6551   int right_x = x + right_dx, right_y = y + right_dy;
6552   int move_x  = x + move_dx,  move_y  = y + move_dy;
6553
6554   int xx, yy;
6555
6556   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6557   {
6558     TestIfBadThingTouchesOtherBadThing(x, y);
6559
6560     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6561       MovDir[x][y] = right_dir;
6562     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6563       MovDir[x][y] = left_dir;
6564
6565     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6566       MovDelay[x][y] = 9;
6567     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6568       MovDelay[x][y] = 1;
6569   }
6570   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6571   {
6572     TestIfBadThingTouchesOtherBadThing(x, y);
6573
6574     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6575       MovDir[x][y] = left_dir;
6576     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6577       MovDir[x][y] = right_dir;
6578
6579     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6580       MovDelay[x][y] = 9;
6581     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6582       MovDelay[x][y] = 1;
6583   }
6584   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6585   {
6586     TestIfBadThingTouchesOtherBadThing(x, y);
6587
6588     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6589       MovDir[x][y] = left_dir;
6590     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6591       MovDir[x][y] = right_dir;
6592
6593     if (MovDir[x][y] != old_move_dir)
6594       MovDelay[x][y] = 9;
6595   }
6596   else if (element == EL_YAMYAM)
6597   {
6598     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6599     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6600
6601     if (can_turn_left && can_turn_right)
6602       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6603     else if (can_turn_left)
6604       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6605     else if (can_turn_right)
6606       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6607     else
6608       MovDir[x][y] = back_dir;
6609
6610     MovDelay[x][y] = 16 + 16 * RND(3);
6611   }
6612   else if (element == EL_DARK_YAMYAM)
6613   {
6614     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6615                                                          left_x, left_y);
6616     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6617                                                          right_x, right_y);
6618
6619     if (can_turn_left && can_turn_right)
6620       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6621     else if (can_turn_left)
6622       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6623     else if (can_turn_right)
6624       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6625     else
6626       MovDir[x][y] = back_dir;
6627
6628     MovDelay[x][y] = 16 + 16 * RND(3);
6629   }
6630   else if (element == EL_PACMAN)
6631   {
6632     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6633     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6634
6635     if (can_turn_left && can_turn_right)
6636       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6637     else if (can_turn_left)
6638       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6639     else if (can_turn_right)
6640       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6641     else
6642       MovDir[x][y] = back_dir;
6643
6644     MovDelay[x][y] = 6 + RND(40);
6645   }
6646   else if (element == EL_PIG)
6647   {
6648     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6649     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6650     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6651     boolean should_turn_left, should_turn_right, should_move_on;
6652     int rnd_value = 24;
6653     int rnd = RND(rnd_value);
6654
6655     should_turn_left = (can_turn_left &&
6656                         (!can_move_on ||
6657                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6658                                                    y + back_dy + left_dy)));
6659     should_turn_right = (can_turn_right &&
6660                          (!can_move_on ||
6661                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6662                                                     y + back_dy + right_dy)));
6663     should_move_on = (can_move_on &&
6664                       (!can_turn_left ||
6665                        !can_turn_right ||
6666                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6667                                                  y + move_dy + left_dy) ||
6668                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6669                                                  y + move_dy + right_dy)));
6670
6671     if (should_turn_left || should_turn_right || should_move_on)
6672     {
6673       if (should_turn_left && should_turn_right && should_move_on)
6674         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6675                         rnd < 2 * rnd_value / 3 ? right_dir :
6676                         old_move_dir);
6677       else if (should_turn_left && should_turn_right)
6678         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6679       else if (should_turn_left && should_move_on)
6680         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6681       else if (should_turn_right && should_move_on)
6682         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6683       else if (should_turn_left)
6684         MovDir[x][y] = left_dir;
6685       else if (should_turn_right)
6686         MovDir[x][y] = right_dir;
6687       else if (should_move_on)
6688         MovDir[x][y] = old_move_dir;
6689     }
6690     else if (can_move_on && rnd > rnd_value / 8)
6691       MovDir[x][y] = old_move_dir;
6692     else if (can_turn_left && can_turn_right)
6693       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6694     else if (can_turn_left && rnd > rnd_value / 8)
6695       MovDir[x][y] = left_dir;
6696     else if (can_turn_right && rnd > rnd_value/8)
6697       MovDir[x][y] = right_dir;
6698     else
6699       MovDir[x][y] = back_dir;
6700
6701     xx = x + move_xy[MovDir[x][y]].dx;
6702     yy = y + move_xy[MovDir[x][y]].dy;
6703
6704     if (!IN_LEV_FIELD(xx, yy) ||
6705         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6706       MovDir[x][y] = old_move_dir;
6707
6708     MovDelay[x][y] = 0;
6709   }
6710   else if (element == EL_DRAGON)
6711   {
6712     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6713     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6714     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6715     int rnd_value = 24;
6716     int rnd = RND(rnd_value);
6717
6718     if (can_move_on && rnd > rnd_value / 8)
6719       MovDir[x][y] = old_move_dir;
6720     else if (can_turn_left && can_turn_right)
6721       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6722     else if (can_turn_left && rnd > rnd_value / 8)
6723       MovDir[x][y] = left_dir;
6724     else if (can_turn_right && rnd > rnd_value / 8)
6725       MovDir[x][y] = right_dir;
6726     else
6727       MovDir[x][y] = back_dir;
6728
6729     xx = x + move_xy[MovDir[x][y]].dx;
6730     yy = y + move_xy[MovDir[x][y]].dy;
6731
6732     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6733       MovDir[x][y] = old_move_dir;
6734
6735     MovDelay[x][y] = 0;
6736   }
6737   else if (element == EL_MOLE)
6738   {
6739     boolean can_move_on =
6740       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6741                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6742                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6743     if (!can_move_on)
6744     {
6745       boolean can_turn_left =
6746         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6747                               IS_AMOEBOID(Feld[left_x][left_y])));
6748
6749       boolean can_turn_right =
6750         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6751                               IS_AMOEBOID(Feld[right_x][right_y])));
6752
6753       if (can_turn_left && can_turn_right)
6754         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6755       else if (can_turn_left)
6756         MovDir[x][y] = left_dir;
6757       else
6758         MovDir[x][y] = right_dir;
6759     }
6760
6761     if (MovDir[x][y] != old_move_dir)
6762       MovDelay[x][y] = 9;
6763   }
6764   else if (element == EL_BALLOON)
6765   {
6766     MovDir[x][y] = game.wind_direction;
6767     MovDelay[x][y] = 0;
6768   }
6769   else if (element == EL_SPRING)
6770   {
6771     if (MovDir[x][y] & MV_HORIZONTAL)
6772     {
6773       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6774           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6775       {
6776         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6777         ResetGfxAnimation(move_x, move_y);
6778         TEST_DrawLevelField(move_x, move_y);
6779
6780         MovDir[x][y] = back_dir;
6781       }
6782       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6783                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6784         MovDir[x][y] = MV_NONE;
6785     }
6786
6787     MovDelay[x][y] = 0;
6788   }
6789   else if (element == EL_ROBOT ||
6790            element == EL_SATELLITE ||
6791            element == EL_PENGUIN ||
6792            element == EL_EMC_ANDROID)
6793   {
6794     int attr_x = -1, attr_y = -1;
6795
6796     if (AllPlayersGone)
6797     {
6798       attr_x = ExitX;
6799       attr_y = ExitY;
6800     }
6801     else
6802     {
6803       int i;
6804
6805       for (i = 0; i < MAX_PLAYERS; i++)
6806       {
6807         struct PlayerInfo *player = &stored_player[i];
6808         int jx = player->jx, jy = player->jy;
6809
6810         if (!player->active)
6811           continue;
6812
6813         if (attr_x == -1 ||
6814             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6815         {
6816           attr_x = jx;
6817           attr_y = jy;
6818         }
6819       }
6820     }
6821
6822     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6823         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6824          game.engine_version < VERSION_IDENT(3,1,0,0)))
6825     {
6826       attr_x = ZX;
6827       attr_y = ZY;
6828     }
6829
6830     if (element == EL_PENGUIN)
6831     {
6832       int i;
6833       static int xy[4][2] =
6834       {
6835         { 0, -1 },
6836         { -1, 0 },
6837         { +1, 0 },
6838         { 0, +1 }
6839       };
6840
6841       for (i = 0; i < NUM_DIRECTIONS; i++)
6842       {
6843         int ex = x + xy[i][0];
6844         int ey = y + xy[i][1];
6845
6846         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6847                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6848                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6849                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6850         {
6851           attr_x = ex;
6852           attr_y = ey;
6853           break;
6854         }
6855       }
6856     }
6857
6858     MovDir[x][y] = MV_NONE;
6859     if (attr_x < x)
6860       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6861     else if (attr_x > x)
6862       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6863     if (attr_y < y)
6864       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6865     else if (attr_y > y)
6866       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6867
6868     if (element == EL_ROBOT)
6869     {
6870       int newx, newy;
6871
6872       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6873         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6874       Moving2Blocked(x, y, &newx, &newy);
6875
6876       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6877         MovDelay[x][y] = 8 + 8 * !RND(3);
6878       else
6879         MovDelay[x][y] = 16;
6880     }
6881     else if (element == EL_PENGUIN)
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 (PENGUIN_CAN_ENTER_FIELD(element, 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 (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6904           return;
6905
6906         MovDir[x][y] = old_move_dir;
6907         return;
6908       }
6909     }
6910     else if (element == EL_SATELLITE)
6911     {
6912       int newx, newy;
6913
6914       MovDelay[x][y] = 1;
6915
6916       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6917       {
6918         boolean first_horiz = RND(2);
6919         int new_move_dir = MovDir[x][y];
6920
6921         MovDir[x][y] =
6922           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6923         Moving2Blocked(x, y, &newx, &newy);
6924
6925         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6926           return;
6927
6928         MovDir[x][y] =
6929           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6930         Moving2Blocked(x, y, &newx, &newy);
6931
6932         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6933           return;
6934
6935         MovDir[x][y] = old_move_dir;
6936         return;
6937       }
6938     }
6939     else if (element == EL_EMC_ANDROID)
6940     {
6941       static int check_pos[16] =
6942       {
6943         -1,             /*  0 => (invalid)          */
6944         7,              /*  1 => MV_LEFT            */
6945         3,              /*  2 => MV_RIGHT           */
6946         -1,             /*  3 => (invalid)          */
6947         1,              /*  4 =>            MV_UP   */
6948         0,              /*  5 => MV_LEFT  | MV_UP   */
6949         2,              /*  6 => MV_RIGHT | MV_UP   */
6950         -1,             /*  7 => (invalid)          */
6951         5,              /*  8 =>            MV_DOWN */
6952         6,              /*  9 => MV_LEFT  | MV_DOWN */
6953         4,              /* 10 => MV_RIGHT | MV_DOWN */
6954         -1,             /* 11 => (invalid)          */
6955         -1,             /* 12 => (invalid)          */
6956         -1,             /* 13 => (invalid)          */
6957         -1,             /* 14 => (invalid)          */
6958         -1,             /* 15 => (invalid)          */
6959       };
6960       static struct
6961       {
6962         int dx, dy;
6963         int dir;
6964       } check_xy[8] =
6965       {
6966         { -1, -1,       MV_LEFT  | MV_UP   },
6967         {  0, -1,                  MV_UP   },
6968         { +1, -1,       MV_RIGHT | MV_UP   },
6969         { +1,  0,       MV_RIGHT           },
6970         { +1, +1,       MV_RIGHT | MV_DOWN },
6971         {  0, +1,                  MV_DOWN },
6972         { -1, +1,       MV_LEFT  | MV_DOWN },
6973         { -1,  0,       MV_LEFT            },
6974       };
6975       int start_pos, check_order;
6976       boolean can_clone = FALSE;
6977       int i;
6978
6979       /* check if there is any free field around current position */
6980       for (i = 0; i < 8; i++)
6981       {
6982         int newx = x + check_xy[i].dx;
6983         int newy = y + check_xy[i].dy;
6984
6985         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6986         {
6987           can_clone = TRUE;
6988
6989           break;
6990         }
6991       }
6992
6993       if (can_clone)            /* randomly find an element to clone */
6994       {
6995         can_clone = FALSE;
6996
6997         start_pos = check_pos[RND(8)];
6998         check_order = (RND(2) ? -1 : +1);
6999
7000         for (i = 0; i < 8; i++)
7001         {
7002           int pos_raw = start_pos + i * check_order;
7003           int pos = (pos_raw + 8) % 8;
7004           int newx = x + check_xy[pos].dx;
7005           int newy = y + check_xy[pos].dy;
7006
7007           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7008           {
7009             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7010             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7011
7012             Store[x][y] = Feld[newx][newy];
7013
7014             can_clone = TRUE;
7015
7016             break;
7017           }
7018         }
7019       }
7020
7021       if (can_clone)            /* randomly find a direction to move */
7022       {
7023         can_clone = FALSE;
7024
7025         start_pos = check_pos[RND(8)];
7026         check_order = (RND(2) ? -1 : +1);
7027
7028         for (i = 0; i < 8; i++)
7029         {
7030           int pos_raw = start_pos + i * 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 (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7037           {
7038             MovDir[x][y] = new_move_dir;
7039             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7040
7041             can_clone = TRUE;
7042
7043             break;
7044           }
7045         }
7046       }
7047
7048       if (can_clone)            /* cloning and moving successful */
7049         return;
7050
7051       /* cannot clone -- try to move towards player */
7052
7053       start_pos = check_pos[MovDir[x][y] & 0x0f];
7054       check_order = (RND(2) ? -1 : +1);
7055
7056       for (i = 0; i < 3; i++)
7057       {
7058         /* first check start_pos, then previous/next or (next/previous) pos */
7059         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7060         int pos = (pos_raw + 8) % 8;
7061         int newx = x + check_xy[pos].dx;
7062         int newy = y + check_xy[pos].dy;
7063         int new_move_dir = check_xy[pos].dir;
7064
7065         if (IS_PLAYER(newx, newy))
7066           break;
7067
7068         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7069         {
7070           MovDir[x][y] = new_move_dir;
7071           MovDelay[x][y] = level.android_move_time * 8 + 1;
7072
7073           break;
7074         }
7075       }
7076     }
7077   }
7078   else if (move_pattern == MV_TURNING_LEFT ||
7079            move_pattern == MV_TURNING_RIGHT ||
7080            move_pattern == MV_TURNING_LEFT_RIGHT ||
7081            move_pattern == MV_TURNING_RIGHT_LEFT ||
7082            move_pattern == MV_TURNING_RANDOM ||
7083            move_pattern == MV_ALL_DIRECTIONS)
7084   {
7085     boolean can_turn_left =
7086       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7087     boolean can_turn_right =
7088       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7089
7090     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7091       return;
7092
7093     if (move_pattern == MV_TURNING_LEFT)
7094       MovDir[x][y] = left_dir;
7095     else if (move_pattern == MV_TURNING_RIGHT)
7096       MovDir[x][y] = right_dir;
7097     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7098       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7099     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7100       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7101     else if (move_pattern == MV_TURNING_RANDOM)
7102       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7103                       can_turn_right && !can_turn_left ? right_dir :
7104                       RND(2) ? left_dir : right_dir);
7105     else if (can_turn_left && can_turn_right)
7106       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7107     else if (can_turn_left)
7108       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7109     else if (can_turn_right)
7110       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7111     else
7112       MovDir[x][y] = back_dir;
7113
7114     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7115   }
7116   else if (move_pattern == MV_HORIZONTAL ||
7117            move_pattern == MV_VERTICAL)
7118   {
7119     if (move_pattern & old_move_dir)
7120       MovDir[x][y] = back_dir;
7121     else if (move_pattern == MV_HORIZONTAL)
7122       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7123     else if (move_pattern == MV_VERTICAL)
7124       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7125
7126     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7127   }
7128   else if (move_pattern & MV_ANY_DIRECTION)
7129   {
7130     MovDir[x][y] = move_pattern;
7131     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7132   }
7133   else if (move_pattern & MV_WIND_DIRECTION)
7134   {
7135     MovDir[x][y] = game.wind_direction;
7136     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7137   }
7138   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7139   {
7140     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7141       MovDir[x][y] = left_dir;
7142     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7143       MovDir[x][y] = right_dir;
7144
7145     if (MovDir[x][y] != old_move_dir)
7146       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7147   }
7148   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7149   {
7150     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7151       MovDir[x][y] = right_dir;
7152     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7153       MovDir[x][y] = left_dir;
7154
7155     if (MovDir[x][y] != old_move_dir)
7156       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7157   }
7158   else if (move_pattern == MV_TOWARDS_PLAYER ||
7159            move_pattern == MV_AWAY_FROM_PLAYER)
7160   {
7161     int attr_x = -1, attr_y = -1;
7162     int newx, newy;
7163     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7164
7165     if (AllPlayersGone)
7166     {
7167       attr_x = ExitX;
7168       attr_y = ExitY;
7169     }
7170     else
7171     {
7172       int i;
7173
7174       for (i = 0; i < MAX_PLAYERS; i++)
7175       {
7176         struct PlayerInfo *player = &stored_player[i];
7177         int jx = player->jx, jy = player->jy;
7178
7179         if (!player->active)
7180           continue;
7181
7182         if (attr_x == -1 ||
7183             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7184         {
7185           attr_x = jx;
7186           attr_y = jy;
7187         }
7188       }
7189     }
7190
7191     MovDir[x][y] = MV_NONE;
7192     if (attr_x < x)
7193       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7194     else if (attr_x > x)
7195       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7196     if (attr_y < y)
7197       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7198     else if (attr_y > y)
7199       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7200
7201     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7202
7203     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7204     {
7205       boolean first_horiz = RND(2);
7206       int new_move_dir = MovDir[x][y];
7207
7208       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7209       {
7210         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7211         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212
7213         return;
7214       }
7215
7216       MovDir[x][y] =
7217         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7218       Moving2Blocked(x, y, &newx, &newy);
7219
7220       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7221         return;
7222
7223       MovDir[x][y] =
7224         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7225       Moving2Blocked(x, y, &newx, &newy);
7226
7227       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7228         return;
7229
7230       MovDir[x][y] = old_move_dir;
7231     }
7232   }
7233   else if (move_pattern == MV_WHEN_PUSHED ||
7234            move_pattern == MV_WHEN_DROPPED)
7235   {
7236     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7237       MovDir[x][y] = MV_NONE;
7238
7239     MovDelay[x][y] = 0;
7240   }
7241   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7242   {
7243     static int test_xy[7][2] =
7244     {
7245       { 0, -1 },
7246       { -1, 0 },
7247       { +1, 0 },
7248       { 0, +1 },
7249       { 0, -1 },
7250       { -1, 0 },
7251       { +1, 0 },
7252     };
7253     static int test_dir[7] =
7254     {
7255       MV_UP,
7256       MV_LEFT,
7257       MV_RIGHT,
7258       MV_DOWN,
7259       MV_UP,
7260       MV_LEFT,
7261       MV_RIGHT,
7262     };
7263     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7264     int move_preference = -1000000;     /* start with very low preference */
7265     int new_move_dir = MV_NONE;
7266     int start_test = RND(4);
7267     int i;
7268
7269     for (i = 0; i < NUM_DIRECTIONS; i++)
7270     {
7271       int move_dir = test_dir[start_test + i];
7272       int move_dir_preference;
7273
7274       xx = x + test_xy[start_test + i][0];
7275       yy = y + test_xy[start_test + i][1];
7276
7277       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7278           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7279       {
7280         new_move_dir = move_dir;
7281
7282         break;
7283       }
7284
7285       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7286         continue;
7287
7288       move_dir_preference = -1 * RunnerVisit[xx][yy];
7289       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7290         move_dir_preference = PlayerVisit[xx][yy];
7291
7292       if (move_dir_preference > move_preference)
7293       {
7294         /* prefer field that has not been visited for the longest time */
7295         move_preference = move_dir_preference;
7296         new_move_dir = move_dir;
7297       }
7298       else if (move_dir_preference == move_preference &&
7299                move_dir == old_move_dir)
7300       {
7301         /* prefer last direction when all directions are preferred equally */
7302         move_preference = move_dir_preference;
7303         new_move_dir = move_dir;
7304       }
7305     }
7306
7307     MovDir[x][y] = new_move_dir;
7308     if (old_move_dir != new_move_dir)
7309       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7310   }
7311 }
7312
7313 static void TurnRound(int x, int y)
7314 {
7315   int direction = MovDir[x][y];
7316
7317   TurnRoundExt(x, y);
7318
7319   GfxDir[x][y] = MovDir[x][y];
7320
7321   if (direction != MovDir[x][y])
7322     GfxFrame[x][y] = 0;
7323
7324   if (MovDelay[x][y])
7325     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7326
7327   ResetGfxFrame(x, y);
7328 }
7329
7330 static boolean JustBeingPushed(int x, int y)
7331 {
7332   int i;
7333
7334   for (i = 0; i < MAX_PLAYERS; i++)
7335   {
7336     struct PlayerInfo *player = &stored_player[i];
7337
7338     if (player->active && player->is_pushing && player->MovPos)
7339     {
7340       int next_jx = player->jx + (player->jx - player->last_jx);
7341       int next_jy = player->jy + (player->jy - player->last_jy);
7342
7343       if (x == next_jx && y == next_jy)
7344         return TRUE;
7345     }
7346   }
7347
7348   return FALSE;
7349 }
7350
7351 void StartMoving(int x, int y)
7352 {
7353   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7354   int element = Feld[x][y];
7355
7356   if (Stop[x][y])
7357     return;
7358
7359   if (MovDelay[x][y] == 0)
7360     GfxAction[x][y] = ACTION_DEFAULT;
7361
7362   if (CAN_FALL(element) && y < lev_fieldy - 1)
7363   {
7364     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7365         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7366       if (JustBeingPushed(x, y))
7367         return;
7368
7369     if (element == EL_QUICKSAND_FULL)
7370     {
7371       if (IS_FREE(x, y + 1))
7372       {
7373         InitMovingField(x, y, MV_DOWN);
7374         started_moving = TRUE;
7375
7376         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7377 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7378         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7379           Store[x][y] = EL_ROCK;
7380 #else
7381         Store[x][y] = EL_ROCK;
7382 #endif
7383
7384         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7385       }
7386       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7387       {
7388         if (!MovDelay[x][y])
7389         {
7390           MovDelay[x][y] = TILEY + 1;
7391
7392           ResetGfxAnimation(x, y);
7393           ResetGfxAnimation(x, y + 1);
7394         }
7395
7396         if (MovDelay[x][y])
7397         {
7398           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7399           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7400
7401           MovDelay[x][y]--;
7402           if (MovDelay[x][y])
7403             return;
7404         }
7405
7406         Feld[x][y] = EL_QUICKSAND_EMPTY;
7407         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7408         Store[x][y + 1] = Store[x][y];
7409         Store[x][y] = 0;
7410
7411         PlayLevelSoundAction(x, y, ACTION_FILLING);
7412       }
7413       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7414       {
7415         if (!MovDelay[x][y])
7416         {
7417           MovDelay[x][y] = TILEY + 1;
7418
7419           ResetGfxAnimation(x, y);
7420           ResetGfxAnimation(x, y + 1);
7421         }
7422
7423         if (MovDelay[x][y])
7424         {
7425           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7426           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7427
7428           MovDelay[x][y]--;
7429           if (MovDelay[x][y])
7430             return;
7431         }
7432
7433         Feld[x][y] = EL_QUICKSAND_EMPTY;
7434         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7435         Store[x][y + 1] = Store[x][y];
7436         Store[x][y] = 0;
7437
7438         PlayLevelSoundAction(x, y, ACTION_FILLING);
7439       }
7440     }
7441     else if (element == EL_QUICKSAND_FAST_FULL)
7442     {
7443       if (IS_FREE(x, y + 1))
7444       {
7445         InitMovingField(x, y, MV_DOWN);
7446         started_moving = TRUE;
7447
7448         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7449 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7450         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7451           Store[x][y] = EL_ROCK;
7452 #else
7453         Store[x][y] = EL_ROCK;
7454 #endif
7455
7456         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7457       }
7458       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7459       {
7460         if (!MovDelay[x][y])
7461         {
7462           MovDelay[x][y] = TILEY + 1;
7463
7464           ResetGfxAnimation(x, y);
7465           ResetGfxAnimation(x, y + 1);
7466         }
7467
7468         if (MovDelay[x][y])
7469         {
7470           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7471           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7472
7473           MovDelay[x][y]--;
7474           if (MovDelay[x][y])
7475             return;
7476         }
7477
7478         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7479         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7480         Store[x][y + 1] = Store[x][y];
7481         Store[x][y] = 0;
7482
7483         PlayLevelSoundAction(x, y, ACTION_FILLING);
7484       }
7485       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7486       {
7487         if (!MovDelay[x][y])
7488         {
7489           MovDelay[x][y] = TILEY + 1;
7490
7491           ResetGfxAnimation(x, y);
7492           ResetGfxAnimation(x, y + 1);
7493         }
7494
7495         if (MovDelay[x][y])
7496         {
7497           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7498           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7499
7500           MovDelay[x][y]--;
7501           if (MovDelay[x][y])
7502             return;
7503         }
7504
7505         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7506         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7507         Store[x][y + 1] = Store[x][y];
7508         Store[x][y] = 0;
7509
7510         PlayLevelSoundAction(x, y, ACTION_FILLING);
7511       }
7512     }
7513     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7514              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7515     {
7516       InitMovingField(x, y, MV_DOWN);
7517       started_moving = TRUE;
7518
7519       Feld[x][y] = EL_QUICKSAND_FILLING;
7520       Store[x][y] = element;
7521
7522       PlayLevelSoundAction(x, y, ACTION_FILLING);
7523     }
7524     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7525              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7526     {
7527       InitMovingField(x, y, MV_DOWN);
7528       started_moving = TRUE;
7529
7530       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7531       Store[x][y] = element;
7532
7533       PlayLevelSoundAction(x, y, ACTION_FILLING);
7534     }
7535     else if (element == EL_MAGIC_WALL_FULL)
7536     {
7537       if (IS_FREE(x, y + 1))
7538       {
7539         InitMovingField(x, y, MV_DOWN);
7540         started_moving = TRUE;
7541
7542         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7543         Store[x][y] = EL_CHANGED(Store[x][y]);
7544       }
7545       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7546       {
7547         if (!MovDelay[x][y])
7548           MovDelay[x][y] = TILEY / 4 + 1;
7549
7550         if (MovDelay[x][y])
7551         {
7552           MovDelay[x][y]--;
7553           if (MovDelay[x][y])
7554             return;
7555         }
7556
7557         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7558         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7559         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7560         Store[x][y] = 0;
7561       }
7562     }
7563     else if (element == EL_BD_MAGIC_WALL_FULL)
7564     {
7565       if (IS_FREE(x, y + 1))
7566       {
7567         InitMovingField(x, y, MV_DOWN);
7568         started_moving = TRUE;
7569
7570         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7571         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7572       }
7573       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7574       {
7575         if (!MovDelay[x][y])
7576           MovDelay[x][y] = TILEY / 4 + 1;
7577
7578         if (MovDelay[x][y])
7579         {
7580           MovDelay[x][y]--;
7581           if (MovDelay[x][y])
7582             return;
7583         }
7584
7585         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7586         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7587         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7588         Store[x][y] = 0;
7589       }
7590     }
7591     else if (element == EL_DC_MAGIC_WALL_FULL)
7592     {
7593       if (IS_FREE(x, y + 1))
7594       {
7595         InitMovingField(x, y, MV_DOWN);
7596         started_moving = TRUE;
7597
7598         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7599         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7600       }
7601       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7602       {
7603         if (!MovDelay[x][y])
7604           MovDelay[x][y] = TILEY / 4 + 1;
7605
7606         if (MovDelay[x][y])
7607         {
7608           MovDelay[x][y]--;
7609           if (MovDelay[x][y])
7610             return;
7611         }
7612
7613         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7614         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7615         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7616         Store[x][y] = 0;
7617       }
7618     }
7619     else if ((CAN_PASS_MAGIC_WALL(element) &&
7620               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7621                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7622              (CAN_PASS_DC_MAGIC_WALL(element) &&
7623               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7624
7625     {
7626       InitMovingField(x, y, MV_DOWN);
7627       started_moving = TRUE;
7628
7629       Feld[x][y] =
7630         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7631          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7632          EL_DC_MAGIC_WALL_FILLING);
7633       Store[x][y] = element;
7634     }
7635     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7636     {
7637       SplashAcid(x, y + 1);
7638
7639       InitMovingField(x, y, MV_DOWN);
7640       started_moving = TRUE;
7641
7642       Store[x][y] = EL_ACID;
7643     }
7644     else if (
7645              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7646               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7647              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7648               CAN_FALL(element) && WasJustFalling[x][y] &&
7649               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7650
7651              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7652               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7653               (Feld[x][y + 1] == EL_BLOCKED)))
7654     {
7655       /* this is needed for a special case not covered by calling "Impact()"
7656          from "ContinueMoving()": if an element moves to a tile directly below
7657          another element which was just falling on that tile (which was empty
7658          in the previous frame), the falling element above would just stop
7659          instead of smashing the element below (in previous version, the above
7660          element was just checked for "moving" instead of "falling", resulting
7661          in incorrect smashes caused by horizontal movement of the above
7662          element; also, the case of the player being the element to smash was
7663          simply not covered here... :-/ ) */
7664
7665       CheckCollision[x][y] = 0;
7666       CheckImpact[x][y] = 0;
7667
7668       Impact(x, y);
7669     }
7670     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7671     {
7672       if (MovDir[x][y] == MV_NONE)
7673       {
7674         InitMovingField(x, y, MV_DOWN);
7675         started_moving = TRUE;
7676       }
7677     }
7678     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7679     {
7680       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7681         MovDir[x][y] = MV_DOWN;
7682
7683       InitMovingField(x, y, MV_DOWN);
7684       started_moving = TRUE;
7685     }
7686     else if (element == EL_AMOEBA_DROP)
7687     {
7688       Feld[x][y] = EL_AMOEBA_GROWING;
7689       Store[x][y] = EL_AMOEBA_WET;
7690     }
7691     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7692               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7693              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7694              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7695     {
7696       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7697                                 (IS_FREE(x - 1, y + 1) ||
7698                                  Feld[x - 1][y + 1] == EL_ACID));
7699       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7700                                 (IS_FREE(x + 1, y + 1) ||
7701                                  Feld[x + 1][y + 1] == EL_ACID));
7702       boolean can_fall_any  = (can_fall_left || can_fall_right);
7703       boolean can_fall_both = (can_fall_left && can_fall_right);
7704       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7705
7706       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7707       {
7708         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7709           can_fall_right = FALSE;
7710         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7711           can_fall_left = FALSE;
7712         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7713           can_fall_right = FALSE;
7714         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7715           can_fall_left = FALSE;
7716
7717         can_fall_any  = (can_fall_left || can_fall_right);
7718         can_fall_both = FALSE;
7719       }
7720
7721       if (can_fall_both)
7722       {
7723         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7724           can_fall_right = FALSE;       /* slip down on left side */
7725         else
7726           can_fall_left = !(can_fall_right = RND(2));
7727
7728         can_fall_both = FALSE;
7729       }
7730
7731       if (can_fall_any)
7732       {
7733         /* if not determined otherwise, prefer left side for slipping down */
7734         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7735         started_moving = TRUE;
7736       }
7737     }
7738     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7739     {
7740       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7741       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7742       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7743       int belt_dir = game.belt_dir[belt_nr];
7744
7745       if ((belt_dir == MV_LEFT  && left_is_free) ||
7746           (belt_dir == MV_RIGHT && right_is_free))
7747       {
7748         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7749
7750         InitMovingField(x, y, belt_dir);
7751         started_moving = TRUE;
7752
7753         Pushed[x][y] = TRUE;
7754         Pushed[nextx][y] = TRUE;
7755
7756         GfxAction[x][y] = ACTION_DEFAULT;
7757       }
7758       else
7759       {
7760         MovDir[x][y] = 0;       /* if element was moving, stop it */
7761       }
7762     }
7763   }
7764
7765   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7766   if (CAN_MOVE(element) && !started_moving)
7767   {
7768     int move_pattern = element_info[element].move_pattern;
7769     int newx, newy;
7770
7771     Moving2Blocked(x, y, &newx, &newy);
7772
7773     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7774       return;
7775
7776     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7777         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7778     {
7779       WasJustMoving[x][y] = 0;
7780       CheckCollision[x][y] = 0;
7781
7782       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7783
7784       if (Feld[x][y] != element)        /* element has changed */
7785         return;
7786     }
7787
7788     if (!MovDelay[x][y])        /* start new movement phase */
7789     {
7790       /* all objects that can change their move direction after each step
7791          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7792
7793       if (element != EL_YAMYAM &&
7794           element != EL_DARK_YAMYAM &&
7795           element != EL_PACMAN &&
7796           !(move_pattern & MV_ANY_DIRECTION) &&
7797           move_pattern != MV_TURNING_LEFT &&
7798           move_pattern != MV_TURNING_RIGHT &&
7799           move_pattern != MV_TURNING_LEFT_RIGHT &&
7800           move_pattern != MV_TURNING_RIGHT_LEFT &&
7801           move_pattern != MV_TURNING_RANDOM)
7802       {
7803         TurnRound(x, y);
7804
7805         if (MovDelay[x][y] && (element == EL_BUG ||
7806                                element == EL_SPACESHIP ||
7807                                element == EL_SP_SNIKSNAK ||
7808                                element == EL_SP_ELECTRON ||
7809                                element == EL_MOLE))
7810           TEST_DrawLevelField(x, y);
7811       }
7812     }
7813
7814     if (MovDelay[x][y])         /* wait some time before next movement */
7815     {
7816       MovDelay[x][y]--;
7817
7818       if (element == EL_ROBOT ||
7819           element == EL_YAMYAM ||
7820           element == EL_DARK_YAMYAM)
7821       {
7822         DrawLevelElementAnimationIfNeeded(x, y, element);
7823         PlayLevelSoundAction(x, y, ACTION_WAITING);
7824       }
7825       else if (element == EL_SP_ELECTRON)
7826         DrawLevelElementAnimationIfNeeded(x, y, element);
7827       else if (element == EL_DRAGON)
7828       {
7829         int i;
7830         int dir = MovDir[x][y];
7831         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7832         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7833         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7834                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7835                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7836                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7837         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7838
7839         GfxAction[x][y] = ACTION_ATTACKING;
7840
7841         if (IS_PLAYER(x, y))
7842           DrawPlayerField(x, y);
7843         else
7844           TEST_DrawLevelField(x, y);
7845
7846         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7847
7848         for (i = 1; i <= 3; i++)
7849         {
7850           int xx = x + i * dx;
7851           int yy = y + i * dy;
7852           int sx = SCREENX(xx);
7853           int sy = SCREENY(yy);
7854           int flame_graphic = graphic + (i - 1);
7855
7856           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7857             break;
7858
7859           if (MovDelay[x][y])
7860           {
7861             int flamed = MovingOrBlocked2Element(xx, yy);
7862
7863             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7864               Bang(xx, yy);
7865             else
7866               RemoveMovingField(xx, yy);
7867
7868             ChangeDelay[xx][yy] = 0;
7869
7870             Feld[xx][yy] = EL_FLAMES;
7871
7872             if (IN_SCR_FIELD(sx, sy))
7873             {
7874               TEST_DrawLevelFieldCrumbled(xx, yy);
7875               DrawGraphic(sx, sy, flame_graphic, frame);
7876             }
7877           }
7878           else
7879           {
7880             if (Feld[xx][yy] == EL_FLAMES)
7881               Feld[xx][yy] = EL_EMPTY;
7882             TEST_DrawLevelField(xx, yy);
7883           }
7884         }
7885       }
7886
7887       if (MovDelay[x][y])       /* element still has to wait some time */
7888       {
7889         PlayLevelSoundAction(x, y, ACTION_WAITING);
7890
7891         return;
7892       }
7893     }
7894
7895     /* now make next step */
7896
7897     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7898
7899     if (DONT_COLLIDE_WITH(element) &&
7900         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7901         !PLAYER_ENEMY_PROTECTED(newx, newy))
7902     {
7903       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7904
7905       return;
7906     }
7907
7908     else if (CAN_MOVE_INTO_ACID(element) &&
7909              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7910              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7911              (MovDir[x][y] == MV_DOWN ||
7912               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7913     {
7914       SplashAcid(newx, newy);
7915       Store[x][y] = EL_ACID;
7916     }
7917     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7918     {
7919       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7920           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7921           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7922           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7923       {
7924         RemoveField(x, y);
7925         TEST_DrawLevelField(x, y);
7926
7927         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7928         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7929           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7930
7931         local_player->friends_still_needed--;
7932         if (!local_player->friends_still_needed &&
7933             !local_player->GameOver && AllPlayersGone)
7934           PlayerWins(local_player);
7935
7936         return;
7937       }
7938       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7939       {
7940         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7941           TEST_DrawLevelField(newx, newy);
7942         else
7943           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7944       }
7945       else if (!IS_FREE(newx, newy))
7946       {
7947         GfxAction[x][y] = ACTION_WAITING;
7948
7949         if (IS_PLAYER(x, y))
7950           DrawPlayerField(x, y);
7951         else
7952           TEST_DrawLevelField(x, y);
7953
7954         return;
7955       }
7956     }
7957     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7958     {
7959       if (IS_FOOD_PIG(Feld[newx][newy]))
7960       {
7961         if (IS_MOVING(newx, newy))
7962           RemoveMovingField(newx, newy);
7963         else
7964         {
7965           Feld[newx][newy] = EL_EMPTY;
7966           TEST_DrawLevelField(newx, newy);
7967         }
7968
7969         PlayLevelSound(x, y, SND_PIG_DIGGING);
7970       }
7971       else if (!IS_FREE(newx, newy))
7972       {
7973         if (IS_PLAYER(x, y))
7974           DrawPlayerField(x, y);
7975         else
7976           TEST_DrawLevelField(x, y);
7977
7978         return;
7979       }
7980     }
7981     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7982     {
7983       if (Store[x][y] != EL_EMPTY)
7984       {
7985         boolean can_clone = FALSE;
7986         int xx, yy;
7987
7988         /* check if element to clone is still there */
7989         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7990         {
7991           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7992           {
7993             can_clone = TRUE;
7994
7995             break;
7996           }
7997         }
7998
7999         /* cannot clone or target field not free anymore -- do not clone */
8000         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8001           Store[x][y] = EL_EMPTY;
8002       }
8003
8004       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8005       {
8006         if (IS_MV_DIAGONAL(MovDir[x][y]))
8007         {
8008           int diagonal_move_dir = MovDir[x][y];
8009           int stored = Store[x][y];
8010           int change_delay = 8;
8011           int graphic;
8012
8013           /* android is moving diagonally */
8014
8015           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8016
8017           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8018           GfxElement[x][y] = EL_EMC_ANDROID;
8019           GfxAction[x][y] = ACTION_SHRINKING;
8020           GfxDir[x][y] = diagonal_move_dir;
8021           ChangeDelay[x][y] = change_delay;
8022
8023           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8024                                    GfxDir[x][y]);
8025
8026           DrawLevelGraphicAnimation(x, y, graphic);
8027           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8028
8029           if (Feld[newx][newy] == EL_ACID)
8030           {
8031             SplashAcid(newx, newy);
8032
8033             return;
8034           }
8035
8036           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8037
8038           Store[newx][newy] = EL_EMC_ANDROID;
8039           GfxElement[newx][newy] = EL_EMC_ANDROID;
8040           GfxAction[newx][newy] = ACTION_GROWING;
8041           GfxDir[newx][newy] = diagonal_move_dir;
8042           ChangeDelay[newx][newy] = change_delay;
8043
8044           graphic = el_act_dir2img(GfxElement[newx][newy],
8045                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8046
8047           DrawLevelGraphicAnimation(newx, newy, graphic);
8048           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8049
8050           return;
8051         }
8052         else
8053         {
8054           Feld[newx][newy] = EL_EMPTY;
8055           TEST_DrawLevelField(newx, newy);
8056
8057           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8058         }
8059       }
8060       else if (!IS_FREE(newx, newy))
8061       {
8062         return;
8063       }
8064     }
8065     else if (IS_CUSTOM_ELEMENT(element) &&
8066              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8067     {
8068       if (!DigFieldByCE(newx, newy, element))
8069         return;
8070
8071       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8072       {
8073         RunnerVisit[x][y] = FrameCounter;
8074         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8075       }
8076     }
8077     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8078     {
8079       if (!IS_FREE(newx, newy))
8080       {
8081         if (IS_PLAYER(x, y))
8082           DrawPlayerField(x, y);
8083         else
8084           TEST_DrawLevelField(x, y);
8085
8086         return;
8087       }
8088       else
8089       {
8090         boolean wanna_flame = !RND(10);
8091         int dx = newx - x, dy = newy - y;
8092         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8093         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8094         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8095                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8096         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8097                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8098
8099         if ((wanna_flame ||
8100              IS_CLASSIC_ENEMY(element1) ||
8101              IS_CLASSIC_ENEMY(element2)) &&
8102             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8103             element1 != EL_FLAMES && element2 != EL_FLAMES)
8104         {
8105           ResetGfxAnimation(x, y);
8106           GfxAction[x][y] = ACTION_ATTACKING;
8107
8108           if (IS_PLAYER(x, y))
8109             DrawPlayerField(x, y);
8110           else
8111             TEST_DrawLevelField(x, y);
8112
8113           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8114
8115           MovDelay[x][y] = 50;
8116
8117           Feld[newx][newy] = EL_FLAMES;
8118           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8119             Feld[newx1][newy1] = EL_FLAMES;
8120           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8121             Feld[newx2][newy2] = EL_FLAMES;
8122
8123           return;
8124         }
8125       }
8126     }
8127     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8128              Feld[newx][newy] == EL_DIAMOND)
8129     {
8130       if (IS_MOVING(newx, newy))
8131         RemoveMovingField(newx, newy);
8132       else
8133       {
8134         Feld[newx][newy] = EL_EMPTY;
8135         TEST_DrawLevelField(newx, newy);
8136       }
8137
8138       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8139     }
8140     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8141              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8142     {
8143       if (AmoebaNr[newx][newy])
8144       {
8145         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8146         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8147             Feld[newx][newy] == EL_BD_AMOEBA)
8148           AmoebaCnt[AmoebaNr[newx][newy]]--;
8149       }
8150
8151       if (IS_MOVING(newx, newy))
8152       {
8153         RemoveMovingField(newx, newy);
8154       }
8155       else
8156       {
8157         Feld[newx][newy] = EL_EMPTY;
8158         TEST_DrawLevelField(newx, newy);
8159       }
8160
8161       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8162     }
8163     else if ((element == EL_PACMAN || element == EL_MOLE)
8164              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8165     {
8166       if (AmoebaNr[newx][newy])
8167       {
8168         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8169         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8170             Feld[newx][newy] == EL_BD_AMOEBA)
8171           AmoebaCnt[AmoebaNr[newx][newy]]--;
8172       }
8173
8174       if (element == EL_MOLE)
8175       {
8176         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8177         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8178
8179         ResetGfxAnimation(x, y);
8180         GfxAction[x][y] = ACTION_DIGGING;
8181         TEST_DrawLevelField(x, y);
8182
8183         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8184
8185         return;                         /* wait for shrinking amoeba */
8186       }
8187       else      /* element == EL_PACMAN */
8188       {
8189         Feld[newx][newy] = EL_EMPTY;
8190         TEST_DrawLevelField(newx, newy);
8191         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8192       }
8193     }
8194     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8195              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8196               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8197     {
8198       /* wait for shrinking amoeba to completely disappear */
8199       return;
8200     }
8201     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8202     {
8203       /* object was running against a wall */
8204
8205       TurnRound(x, y);
8206
8207       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8208         DrawLevelElementAnimation(x, y, element);
8209
8210       if (DONT_TOUCH(element))
8211         TestIfBadThingTouchesPlayer(x, y);
8212
8213       return;
8214     }
8215
8216     InitMovingField(x, y, MovDir[x][y]);
8217
8218     PlayLevelSoundAction(x, y, ACTION_MOVING);
8219   }
8220
8221   if (MovDir[x][y])
8222     ContinueMoving(x, y);
8223 }
8224
8225 void ContinueMoving(int x, int y)
8226 {
8227   int element = Feld[x][y];
8228   struct ElementInfo *ei = &element_info[element];
8229   int direction = MovDir[x][y];
8230   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8231   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8232   int newx = x + dx, newy = y + dy;
8233   int stored = Store[x][y];
8234   int stored_new = Store[newx][newy];
8235   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8236   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8237   boolean last_line = (newy == lev_fieldy - 1);
8238
8239   MovPos[x][y] += getElementMoveStepsize(x, y);
8240
8241   if (pushed_by_player) /* special case: moving object pushed by player */
8242     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8243
8244   if (ABS(MovPos[x][y]) < TILEX)
8245   {
8246     TEST_DrawLevelField(x, y);
8247
8248     return;     /* element is still moving */
8249   }
8250
8251   /* element reached destination field */
8252
8253   Feld[x][y] = EL_EMPTY;
8254   Feld[newx][newy] = element;
8255   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8256
8257   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8258   {
8259     element = Feld[newx][newy] = EL_ACID;
8260   }
8261   else if (element == EL_MOLE)
8262   {
8263     Feld[x][y] = EL_SAND;
8264
8265     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8266   }
8267   else if (element == EL_QUICKSAND_FILLING)
8268   {
8269     element = Feld[newx][newy] = get_next_element(element);
8270     Store[newx][newy] = Store[x][y];
8271   }
8272   else if (element == EL_QUICKSAND_EMPTYING)
8273   {
8274     Feld[x][y] = get_next_element(element);
8275     element = Feld[newx][newy] = Store[x][y];
8276   }
8277   else if (element == EL_QUICKSAND_FAST_FILLING)
8278   {
8279     element = Feld[newx][newy] = get_next_element(element);
8280     Store[newx][newy] = Store[x][y];
8281   }
8282   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8283   {
8284     Feld[x][y] = get_next_element(element);
8285     element = Feld[newx][newy] = Store[x][y];
8286   }
8287   else if (element == EL_MAGIC_WALL_FILLING)
8288   {
8289     element = Feld[newx][newy] = get_next_element(element);
8290     if (!game.magic_wall_active)
8291       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8292     Store[newx][newy] = Store[x][y];
8293   }
8294   else if (element == EL_MAGIC_WALL_EMPTYING)
8295   {
8296     Feld[x][y] = get_next_element(element);
8297     if (!game.magic_wall_active)
8298       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8299     element = Feld[newx][newy] = Store[x][y];
8300
8301     InitField(newx, newy, FALSE);
8302   }
8303   else if (element == EL_BD_MAGIC_WALL_FILLING)
8304   {
8305     element = Feld[newx][newy] = get_next_element(element);
8306     if (!game.magic_wall_active)
8307       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8308     Store[newx][newy] = Store[x][y];
8309   }
8310   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8311   {
8312     Feld[x][y] = get_next_element(element);
8313     if (!game.magic_wall_active)
8314       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8315     element = Feld[newx][newy] = Store[x][y];
8316
8317     InitField(newx, newy, FALSE);
8318   }
8319   else if (element == EL_DC_MAGIC_WALL_FILLING)
8320   {
8321     element = Feld[newx][newy] = get_next_element(element);
8322     if (!game.magic_wall_active)
8323       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8324     Store[newx][newy] = Store[x][y];
8325   }
8326   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8327   {
8328     Feld[x][y] = get_next_element(element);
8329     if (!game.magic_wall_active)
8330       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8331     element = Feld[newx][newy] = Store[x][y];
8332
8333     InitField(newx, newy, FALSE);
8334   }
8335   else if (element == EL_AMOEBA_DROPPING)
8336   {
8337     Feld[x][y] = get_next_element(element);
8338     element = Feld[newx][newy] = Store[x][y];
8339   }
8340   else if (element == EL_SOKOBAN_OBJECT)
8341   {
8342     if (Back[x][y])
8343       Feld[x][y] = Back[x][y];
8344
8345     if (Back[newx][newy])
8346       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8347
8348     Back[x][y] = Back[newx][newy] = 0;
8349   }
8350
8351   Store[x][y] = EL_EMPTY;
8352   MovPos[x][y] = 0;
8353   MovDir[x][y] = 0;
8354   MovDelay[x][y] = 0;
8355
8356   MovDelay[newx][newy] = 0;
8357
8358   if (CAN_CHANGE_OR_HAS_ACTION(element))
8359   {
8360     /* copy element change control values to new field */
8361     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8362     ChangePage[newx][newy]  = ChangePage[x][y];
8363     ChangeCount[newx][newy] = ChangeCount[x][y];
8364     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8365   }
8366
8367   CustomValue[newx][newy] = CustomValue[x][y];
8368
8369   ChangeDelay[x][y] = 0;
8370   ChangePage[x][y] = -1;
8371   ChangeCount[x][y] = 0;
8372   ChangeEvent[x][y] = -1;
8373
8374   CustomValue[x][y] = 0;
8375
8376   /* copy animation control values to new field */
8377   GfxFrame[newx][newy]  = GfxFrame[x][y];
8378   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8379   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8380   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8381
8382   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8383
8384   /* some elements can leave other elements behind after moving */
8385   if (ei->move_leave_element != EL_EMPTY &&
8386       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8387       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8388   {
8389     int move_leave_element = ei->move_leave_element;
8390
8391     /* this makes it possible to leave the removed element again */
8392     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8393       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8394
8395     Feld[x][y] = move_leave_element;
8396
8397     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8398       MovDir[x][y] = direction;
8399
8400     InitField(x, y, FALSE);
8401
8402     if (GFX_CRUMBLED(Feld[x][y]))
8403       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8404
8405     if (ELEM_IS_PLAYER(move_leave_element))
8406       RelocatePlayer(x, y, move_leave_element);
8407   }
8408
8409   /* do this after checking for left-behind element */
8410   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8411
8412   if (!CAN_MOVE(element) ||
8413       (CAN_FALL(element) && direction == MV_DOWN &&
8414        (element == EL_SPRING ||
8415         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8416         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8417     GfxDir[x][y] = MovDir[newx][newy] = 0;
8418
8419   TEST_DrawLevelField(x, y);
8420   TEST_DrawLevelField(newx, newy);
8421
8422   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8423
8424   /* prevent pushed element from moving on in pushed direction */
8425   if (pushed_by_player && CAN_MOVE(element) &&
8426       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8427       !(element_info[element].move_pattern & direction))
8428     TurnRound(newx, newy);
8429
8430   /* prevent elements on conveyor belt from moving on in last direction */
8431   if (pushed_by_conveyor && CAN_FALL(element) &&
8432       direction & MV_HORIZONTAL)
8433     MovDir[newx][newy] = 0;
8434
8435   if (!pushed_by_player)
8436   {
8437     int nextx = newx + dx, nexty = newy + dy;
8438     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8439
8440     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8441
8442     if (CAN_FALL(element) && direction == MV_DOWN)
8443       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8444
8445     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8446       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8447
8448     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8449       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8450   }
8451
8452   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8453   {
8454     TestIfBadThingTouchesPlayer(newx, newy);
8455     TestIfBadThingTouchesFriend(newx, newy);
8456
8457     if (!IS_CUSTOM_ELEMENT(element))
8458       TestIfBadThingTouchesOtherBadThing(newx, newy);
8459   }
8460   else if (element == EL_PENGUIN)
8461     TestIfFriendTouchesBadThing(newx, newy);
8462
8463   if (DONT_GET_HIT_BY(element))
8464   {
8465     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8466   }
8467
8468   /* give the player one last chance (one more frame) to move away */
8469   if (CAN_FALL(element) && direction == MV_DOWN &&
8470       (last_line || (!IS_FREE(x, newy + 1) &&
8471                      (!IS_PLAYER(x, newy + 1) ||
8472                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8473     Impact(x, newy);
8474
8475   if (pushed_by_player && !game.use_change_when_pushing_bug)
8476   {
8477     int push_side = MV_DIR_OPPOSITE(direction);
8478     struct PlayerInfo *player = PLAYERINFO(x, y);
8479
8480     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8481                                player->index_bit, push_side);
8482     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8483                                         player->index_bit, push_side);
8484   }
8485
8486   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8487     MovDelay[newx][newy] = 1;
8488
8489   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8490
8491   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8492   TestIfElementHitsCustomElement(newx, newy, direction);
8493   TestIfPlayerTouchesCustomElement(newx, newy);
8494   TestIfElementTouchesCustomElement(newx, newy);
8495
8496   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8497       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8498     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8499                              MV_DIR_OPPOSITE(direction));
8500 }
8501
8502 int AmoebeNachbarNr(int ax, int ay)
8503 {
8504   int i;
8505   int element = Feld[ax][ay];
8506   int group_nr = 0;
8507   static int xy[4][2] =
8508   {
8509     { 0, -1 },
8510     { -1, 0 },
8511     { +1, 0 },
8512     { 0, +1 }
8513   };
8514
8515   for (i = 0; i < NUM_DIRECTIONS; i++)
8516   {
8517     int x = ax + xy[i][0];
8518     int y = ay + xy[i][1];
8519
8520     if (!IN_LEV_FIELD(x, y))
8521       continue;
8522
8523     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8524       group_nr = AmoebaNr[x][y];
8525   }
8526
8527   return group_nr;
8528 }
8529
8530 void AmoebenVereinigen(int ax, int ay)
8531 {
8532   int i, x, y, xx, yy;
8533   int new_group_nr = AmoebaNr[ax][ay];
8534   static int xy[4][2] =
8535   {
8536     { 0, -1 },
8537     { -1, 0 },
8538     { +1, 0 },
8539     { 0, +1 }
8540   };
8541
8542   if (new_group_nr == 0)
8543     return;
8544
8545   for (i = 0; i < NUM_DIRECTIONS; i++)
8546   {
8547     x = ax + xy[i][0];
8548     y = ay + xy[i][1];
8549
8550     if (!IN_LEV_FIELD(x, y))
8551       continue;
8552
8553     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8554          Feld[x][y] == EL_BD_AMOEBA ||
8555          Feld[x][y] == EL_AMOEBA_DEAD) &&
8556         AmoebaNr[x][y] != new_group_nr)
8557     {
8558       int old_group_nr = AmoebaNr[x][y];
8559
8560       if (old_group_nr == 0)
8561         return;
8562
8563       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8564       AmoebaCnt[old_group_nr] = 0;
8565       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8566       AmoebaCnt2[old_group_nr] = 0;
8567
8568       SCAN_PLAYFIELD(xx, yy)
8569       {
8570         if (AmoebaNr[xx][yy] == old_group_nr)
8571           AmoebaNr[xx][yy] = new_group_nr;
8572       }
8573     }
8574   }
8575 }
8576
8577 void AmoebeUmwandeln(int ax, int ay)
8578 {
8579   int i, x, y;
8580
8581   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8582   {
8583     int group_nr = AmoebaNr[ax][ay];
8584
8585 #ifdef DEBUG
8586     if (group_nr == 0)
8587     {
8588       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8589       printf("AmoebeUmwandeln(): This should never happen!\n");
8590       return;
8591     }
8592 #endif
8593
8594     SCAN_PLAYFIELD(x, y)
8595     {
8596       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8597       {
8598         AmoebaNr[x][y] = 0;
8599         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8600       }
8601     }
8602
8603     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8604                             SND_AMOEBA_TURNING_TO_GEM :
8605                             SND_AMOEBA_TURNING_TO_ROCK));
8606     Bang(ax, ay);
8607   }
8608   else
8609   {
8610     static int xy[4][2] =
8611     {
8612       { 0, -1 },
8613       { -1, 0 },
8614       { +1, 0 },
8615       { 0, +1 }
8616     };
8617
8618     for (i = 0; i < NUM_DIRECTIONS; i++)
8619     {
8620       x = ax + xy[i][0];
8621       y = ay + xy[i][1];
8622
8623       if (!IN_LEV_FIELD(x, y))
8624         continue;
8625
8626       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8627       {
8628         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8629                               SND_AMOEBA_TURNING_TO_GEM :
8630                               SND_AMOEBA_TURNING_TO_ROCK));
8631         Bang(x, y);
8632       }
8633     }
8634   }
8635 }
8636
8637 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8638 {
8639   int x, y;
8640   int group_nr = AmoebaNr[ax][ay];
8641   boolean done = FALSE;
8642
8643 #ifdef DEBUG
8644   if (group_nr == 0)
8645   {
8646     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8647     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8648     return;
8649   }
8650 #endif
8651
8652   SCAN_PLAYFIELD(x, y)
8653   {
8654     if (AmoebaNr[x][y] == group_nr &&
8655         (Feld[x][y] == EL_AMOEBA_DEAD ||
8656          Feld[x][y] == EL_BD_AMOEBA ||
8657          Feld[x][y] == EL_AMOEBA_GROWING))
8658     {
8659       AmoebaNr[x][y] = 0;
8660       Feld[x][y] = new_element;
8661       InitField(x, y, FALSE);
8662       TEST_DrawLevelField(x, y);
8663       done = TRUE;
8664     }
8665   }
8666
8667   if (done)
8668     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8669                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8670                             SND_BD_AMOEBA_TURNING_TO_GEM));
8671 }
8672
8673 void AmoebeWaechst(int x, int y)
8674 {
8675   static unsigned int sound_delay = 0;
8676   static unsigned int sound_delay_value = 0;
8677
8678   if (!MovDelay[x][y])          /* start new growing cycle */
8679   {
8680     MovDelay[x][y] = 7;
8681
8682     if (DelayReached(&sound_delay, sound_delay_value))
8683     {
8684       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8685       sound_delay_value = 30;
8686     }
8687   }
8688
8689   if (MovDelay[x][y])           /* wait some time before growing bigger */
8690   {
8691     MovDelay[x][y]--;
8692     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8693     {
8694       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8695                                            6 - MovDelay[x][y]);
8696
8697       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8698     }
8699
8700     if (!MovDelay[x][y])
8701     {
8702       Feld[x][y] = Store[x][y];
8703       Store[x][y] = 0;
8704       TEST_DrawLevelField(x, y);
8705     }
8706   }
8707 }
8708
8709 void AmoebaDisappearing(int x, int y)
8710 {
8711   static unsigned int sound_delay = 0;
8712   static unsigned int sound_delay_value = 0;
8713
8714   if (!MovDelay[x][y])          /* start new shrinking cycle */
8715   {
8716     MovDelay[x][y] = 7;
8717
8718     if (DelayReached(&sound_delay, sound_delay_value))
8719       sound_delay_value = 30;
8720   }
8721
8722   if (MovDelay[x][y])           /* wait some time before shrinking */
8723   {
8724     MovDelay[x][y]--;
8725     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8726     {
8727       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8728                                            6 - MovDelay[x][y]);
8729
8730       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8731     }
8732
8733     if (!MovDelay[x][y])
8734     {
8735       Feld[x][y] = EL_EMPTY;
8736       TEST_DrawLevelField(x, y);
8737
8738       /* don't let mole enter this field in this cycle;
8739          (give priority to objects falling to this field from above) */
8740       Stop[x][y] = TRUE;
8741     }
8742   }
8743 }
8744
8745 void AmoebeAbleger(int ax, int ay)
8746 {
8747   int i;
8748   int element = Feld[ax][ay];
8749   int graphic = el2img(element);
8750   int newax = ax, neway = ay;
8751   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8752   static int xy[4][2] =
8753   {
8754     { 0, -1 },
8755     { -1, 0 },
8756     { +1, 0 },
8757     { 0, +1 }
8758   };
8759
8760   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8761   {
8762     Feld[ax][ay] = EL_AMOEBA_DEAD;
8763     TEST_DrawLevelField(ax, ay);
8764     return;
8765   }
8766
8767   if (IS_ANIMATED(graphic))
8768     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8769
8770   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8771     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8772
8773   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8774   {
8775     MovDelay[ax][ay]--;
8776     if (MovDelay[ax][ay])
8777       return;
8778   }
8779
8780   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8781   {
8782     int start = RND(4);
8783     int x = ax + xy[start][0];
8784     int y = ay + xy[start][1];
8785
8786     if (!IN_LEV_FIELD(x, y))
8787       return;
8788
8789     if (IS_FREE(x, y) ||
8790         CAN_GROW_INTO(Feld[x][y]) ||
8791         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8792         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8793     {
8794       newax = x;
8795       neway = y;
8796     }
8797
8798     if (newax == ax && neway == ay)
8799       return;
8800   }
8801   else                          /* normal or "filled" (BD style) amoeba */
8802   {
8803     int start = RND(4);
8804     boolean waiting_for_player = FALSE;
8805
8806     for (i = 0; i < NUM_DIRECTIONS; i++)
8807     {
8808       int j = (start + i) % 4;
8809       int x = ax + xy[j][0];
8810       int y = ay + xy[j][1];
8811
8812       if (!IN_LEV_FIELD(x, y))
8813         continue;
8814
8815       if (IS_FREE(x, y) ||
8816           CAN_GROW_INTO(Feld[x][y]) ||
8817           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8818           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8819       {
8820         newax = x;
8821         neway = y;
8822         break;
8823       }
8824       else if (IS_PLAYER(x, y))
8825         waiting_for_player = TRUE;
8826     }
8827
8828     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8829     {
8830       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8831       {
8832         Feld[ax][ay] = EL_AMOEBA_DEAD;
8833         TEST_DrawLevelField(ax, ay);
8834         AmoebaCnt[AmoebaNr[ax][ay]]--;
8835
8836         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8837         {
8838           if (element == EL_AMOEBA_FULL)
8839             AmoebeUmwandeln(ax, ay);
8840           else if (element == EL_BD_AMOEBA)
8841             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8842         }
8843       }
8844       return;
8845     }
8846     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8847     {
8848       /* amoeba gets larger by growing in some direction */
8849
8850       int new_group_nr = AmoebaNr[ax][ay];
8851
8852 #ifdef DEBUG
8853   if (new_group_nr == 0)
8854   {
8855     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8856     printf("AmoebeAbleger(): This should never happen!\n");
8857     return;
8858   }
8859 #endif
8860
8861       AmoebaNr[newax][neway] = new_group_nr;
8862       AmoebaCnt[new_group_nr]++;
8863       AmoebaCnt2[new_group_nr]++;
8864
8865       /* if amoeba touches other amoeba(s) after growing, unify them */
8866       AmoebenVereinigen(newax, neway);
8867
8868       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8869       {
8870         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8871         return;
8872       }
8873     }
8874   }
8875
8876   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8877       (neway == lev_fieldy - 1 && newax != ax))
8878   {
8879     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8880     Store[newax][neway] = element;
8881   }
8882   else if (neway == ay || element == EL_EMC_DRIPPER)
8883   {
8884     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8885
8886     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8887   }
8888   else
8889   {
8890     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8891     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8892     Store[ax][ay] = EL_AMOEBA_DROP;
8893     ContinueMoving(ax, ay);
8894     return;
8895   }
8896
8897   TEST_DrawLevelField(newax, neway);
8898 }
8899
8900 void Life(int ax, int ay)
8901 {
8902   int x1, y1, x2, y2;
8903   int life_time = 40;
8904   int element = Feld[ax][ay];
8905   int graphic = el2img(element);
8906   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8907                          level.biomaze);
8908   boolean changed = FALSE;
8909
8910   if (IS_ANIMATED(graphic))
8911     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8912
8913   if (Stop[ax][ay])
8914     return;
8915
8916   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8917     MovDelay[ax][ay] = life_time;
8918
8919   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8920   {
8921     MovDelay[ax][ay]--;
8922     if (MovDelay[ax][ay])
8923       return;
8924   }
8925
8926   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8927   {
8928     int xx = ax+x1, yy = ay+y1;
8929     int nachbarn = 0;
8930
8931     if (!IN_LEV_FIELD(xx, yy))
8932       continue;
8933
8934     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8935     {
8936       int x = xx+x2, y = yy+y2;
8937
8938       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8939         continue;
8940
8941       if (((Feld[x][y] == element ||
8942             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8943            !Stop[x][y]) ||
8944           (IS_FREE(x, y) && Stop[x][y]))
8945         nachbarn++;
8946     }
8947
8948     if (xx == ax && yy == ay)           /* field in the middle */
8949     {
8950       if (nachbarn < life_parameter[0] ||
8951           nachbarn > life_parameter[1])
8952       {
8953         Feld[xx][yy] = EL_EMPTY;
8954         if (!Stop[xx][yy])
8955           TEST_DrawLevelField(xx, yy);
8956         Stop[xx][yy] = TRUE;
8957         changed = TRUE;
8958       }
8959     }
8960     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8961     {                                   /* free border field */
8962       if (nachbarn >= life_parameter[2] &&
8963           nachbarn <= life_parameter[3])
8964       {
8965         Feld[xx][yy] = element;
8966         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8967         if (!Stop[xx][yy])
8968           TEST_DrawLevelField(xx, yy);
8969         Stop[xx][yy] = TRUE;
8970         changed = TRUE;
8971       }
8972     }
8973   }
8974
8975   if (changed)
8976     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8977                    SND_GAME_OF_LIFE_GROWING);
8978 }
8979
8980 static void InitRobotWheel(int x, int y)
8981 {
8982   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8983 }
8984
8985 static void RunRobotWheel(int x, int y)
8986 {
8987   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8988 }
8989
8990 static void StopRobotWheel(int x, int y)
8991 {
8992   if (ZX == x && ZY == y)
8993   {
8994     ZX = ZY = -1;
8995
8996     game.robot_wheel_active = FALSE;
8997   }
8998 }
8999
9000 static void InitTimegateWheel(int x, int y)
9001 {
9002   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9003 }
9004
9005 static void RunTimegateWheel(int x, int y)
9006 {
9007   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9008 }
9009
9010 static void InitMagicBallDelay(int x, int y)
9011 {
9012   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9013 }
9014
9015 static void ActivateMagicBall(int bx, int by)
9016 {
9017   int x, y;
9018
9019   if (level.ball_random)
9020   {
9021     int pos_border = RND(8);    /* select one of the eight border elements */
9022     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9023     int xx = pos_content % 3;
9024     int yy = pos_content / 3;
9025
9026     x = bx - 1 + xx;
9027     y = by - 1 + yy;
9028
9029     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9030       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9031   }
9032   else
9033   {
9034     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9035     {
9036       int xx = x - bx + 1;
9037       int yy = y - by + 1;
9038
9039       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9040         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9041     }
9042   }
9043
9044   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9045 }
9046
9047 void CheckExit(int x, int y)
9048 {
9049   if (local_player->gems_still_needed > 0 ||
9050       local_player->sokobanfields_still_needed > 0 ||
9051       local_player->lights_still_needed > 0)
9052   {
9053     int element = Feld[x][y];
9054     int graphic = el2img(element);
9055
9056     if (IS_ANIMATED(graphic))
9057       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9058
9059     return;
9060   }
9061
9062   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9063     return;
9064
9065   Feld[x][y] = EL_EXIT_OPENING;
9066
9067   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9068 }
9069
9070 void CheckExitEM(int x, int y)
9071 {
9072   if (local_player->gems_still_needed > 0 ||
9073       local_player->sokobanfields_still_needed > 0 ||
9074       local_player->lights_still_needed > 0)
9075   {
9076     int element = Feld[x][y];
9077     int graphic = el2img(element);
9078
9079     if (IS_ANIMATED(graphic))
9080       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9081
9082     return;
9083   }
9084
9085   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9086     return;
9087
9088   Feld[x][y] = EL_EM_EXIT_OPENING;
9089
9090   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9091 }
9092
9093 void CheckExitSteel(int x, int y)
9094 {
9095   if (local_player->gems_still_needed > 0 ||
9096       local_player->sokobanfields_still_needed > 0 ||
9097       local_player->lights_still_needed > 0)
9098   {
9099     int element = Feld[x][y];
9100     int graphic = el2img(element);
9101
9102     if (IS_ANIMATED(graphic))
9103       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9104
9105     return;
9106   }
9107
9108   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9109     return;
9110
9111   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9112
9113   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9114 }
9115
9116 void CheckExitSteelEM(int x, int y)
9117 {
9118   if (local_player->gems_still_needed > 0 ||
9119       local_player->sokobanfields_still_needed > 0 ||
9120       local_player->lights_still_needed > 0)
9121   {
9122     int element = Feld[x][y];
9123     int graphic = el2img(element);
9124
9125     if (IS_ANIMATED(graphic))
9126       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9127
9128     return;
9129   }
9130
9131   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9132     return;
9133
9134   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9135
9136   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9137 }
9138
9139 void CheckExitSP(int x, int y)
9140 {
9141   if (local_player->gems_still_needed > 0)
9142   {
9143     int element = Feld[x][y];
9144     int graphic = el2img(element);
9145
9146     if (IS_ANIMATED(graphic))
9147       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9148
9149     return;
9150   }
9151
9152   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9153     return;
9154
9155   Feld[x][y] = EL_SP_EXIT_OPENING;
9156
9157   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9158 }
9159
9160 static void CloseAllOpenTimegates()
9161 {
9162   int x, y;
9163
9164   SCAN_PLAYFIELD(x, y)
9165   {
9166     int element = Feld[x][y];
9167
9168     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9169     {
9170       Feld[x][y] = EL_TIMEGATE_CLOSING;
9171
9172       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9173     }
9174   }
9175 }
9176
9177 void DrawTwinkleOnField(int x, int y)
9178 {
9179   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9180     return;
9181
9182   if (Feld[x][y] == EL_BD_DIAMOND)
9183     return;
9184
9185   if (MovDelay[x][y] == 0)      /* next animation frame */
9186     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9187
9188   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9189   {
9190     MovDelay[x][y]--;
9191
9192     DrawLevelElementAnimation(x, y, Feld[x][y]);
9193
9194     if (MovDelay[x][y] != 0)
9195     {
9196       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9197                                            10 - MovDelay[x][y]);
9198
9199       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9200     }
9201   }
9202 }
9203
9204 void MauerWaechst(int x, int y)
9205 {
9206   int delay = 6;
9207
9208   if (!MovDelay[x][y])          /* next animation frame */
9209     MovDelay[x][y] = 3 * delay;
9210
9211   if (MovDelay[x][y])           /* wait some time before next frame */
9212   {
9213     MovDelay[x][y]--;
9214
9215     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9216     {
9217       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9218       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9219
9220       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9221     }
9222
9223     if (!MovDelay[x][y])
9224     {
9225       if (MovDir[x][y] == MV_LEFT)
9226       {
9227         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9228           TEST_DrawLevelField(x - 1, y);
9229       }
9230       else if (MovDir[x][y] == MV_RIGHT)
9231       {
9232         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9233           TEST_DrawLevelField(x + 1, y);
9234       }
9235       else if (MovDir[x][y] == MV_UP)
9236       {
9237         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9238           TEST_DrawLevelField(x, y - 1);
9239       }
9240       else
9241       {
9242         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9243           TEST_DrawLevelField(x, y + 1);
9244       }
9245
9246       Feld[x][y] = Store[x][y];
9247       Store[x][y] = 0;
9248       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9249       TEST_DrawLevelField(x, y);
9250     }
9251   }
9252 }
9253
9254 void MauerAbleger(int ax, int ay)
9255 {
9256   int element = Feld[ax][ay];
9257   int graphic = el2img(element);
9258   boolean oben_frei = FALSE, unten_frei = FALSE;
9259   boolean links_frei = FALSE, rechts_frei = FALSE;
9260   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9261   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9262   boolean new_wall = FALSE;
9263
9264   if (IS_ANIMATED(graphic))
9265     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9266
9267   if (!MovDelay[ax][ay])        /* start building new wall */
9268     MovDelay[ax][ay] = 6;
9269
9270   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9271   {
9272     MovDelay[ax][ay]--;
9273     if (MovDelay[ax][ay])
9274       return;
9275   }
9276
9277   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9278     oben_frei = TRUE;
9279   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9280     unten_frei = TRUE;
9281   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9282     links_frei = TRUE;
9283   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9284     rechts_frei = TRUE;
9285
9286   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9287       element == EL_EXPANDABLE_WALL_ANY)
9288   {
9289     if (oben_frei)
9290     {
9291       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9292       Store[ax][ay-1] = element;
9293       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9294       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9295         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9296                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9297       new_wall = TRUE;
9298     }
9299     if (unten_frei)
9300     {
9301       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9302       Store[ax][ay+1] = element;
9303       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9304       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9305         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9306                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9307       new_wall = TRUE;
9308     }
9309   }
9310
9311   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9312       element == EL_EXPANDABLE_WALL_ANY ||
9313       element == EL_EXPANDABLE_WALL ||
9314       element == EL_BD_EXPANDABLE_WALL)
9315   {
9316     if (links_frei)
9317     {
9318       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9319       Store[ax-1][ay] = element;
9320       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9321       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9322         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9323                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9324       new_wall = TRUE;
9325     }
9326
9327     if (rechts_frei)
9328     {
9329       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9330       Store[ax+1][ay] = element;
9331       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9332       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9333         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9334                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9335       new_wall = TRUE;
9336     }
9337   }
9338
9339   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9340     TEST_DrawLevelField(ax, ay);
9341
9342   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9343     oben_massiv = TRUE;
9344   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9345     unten_massiv = TRUE;
9346   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9347     links_massiv = TRUE;
9348   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9349     rechts_massiv = TRUE;
9350
9351   if (((oben_massiv && unten_massiv) ||
9352        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9353        element == EL_EXPANDABLE_WALL) &&
9354       ((links_massiv && rechts_massiv) ||
9355        element == EL_EXPANDABLE_WALL_VERTICAL))
9356     Feld[ax][ay] = EL_WALL;
9357
9358   if (new_wall)
9359     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9360 }
9361
9362 void MauerAblegerStahl(int ax, int ay)
9363 {
9364   int element = Feld[ax][ay];
9365   int graphic = el2img(element);
9366   boolean oben_frei = FALSE, unten_frei = FALSE;
9367   boolean links_frei = FALSE, rechts_frei = FALSE;
9368   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9369   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9370   boolean new_wall = FALSE;
9371
9372   if (IS_ANIMATED(graphic))
9373     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9374
9375   if (!MovDelay[ax][ay])        /* start building new wall */
9376     MovDelay[ax][ay] = 6;
9377
9378   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9379   {
9380     MovDelay[ax][ay]--;
9381     if (MovDelay[ax][ay])
9382       return;
9383   }
9384
9385   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9386     oben_frei = TRUE;
9387   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9388     unten_frei = TRUE;
9389   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9390     links_frei = TRUE;
9391   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9392     rechts_frei = TRUE;
9393
9394   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9395       element == EL_EXPANDABLE_STEELWALL_ANY)
9396   {
9397     if (oben_frei)
9398     {
9399       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9400       Store[ax][ay-1] = element;
9401       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9402       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9403         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9404                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9405       new_wall = TRUE;
9406     }
9407     if (unten_frei)
9408     {
9409       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9410       Store[ax][ay+1] = element;
9411       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9412       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9413         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9414                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9415       new_wall = TRUE;
9416     }
9417   }
9418
9419   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9420       element == EL_EXPANDABLE_STEELWALL_ANY)
9421   {
9422     if (links_frei)
9423     {
9424       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9425       Store[ax-1][ay] = element;
9426       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9427       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9428         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9429                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9430       new_wall = TRUE;
9431     }
9432
9433     if (rechts_frei)
9434     {
9435       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9436       Store[ax+1][ay] = element;
9437       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9438       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9439         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9440                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9441       new_wall = TRUE;
9442     }
9443   }
9444
9445   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9446     oben_massiv = TRUE;
9447   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9448     unten_massiv = TRUE;
9449   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9450     links_massiv = TRUE;
9451   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9452     rechts_massiv = TRUE;
9453
9454   if (((oben_massiv && unten_massiv) ||
9455        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9456       ((links_massiv && rechts_massiv) ||
9457        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9458     Feld[ax][ay] = EL_STEELWALL;
9459
9460   if (new_wall)
9461     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9462 }
9463
9464 void CheckForDragon(int x, int y)
9465 {
9466   int i, j;
9467   boolean dragon_found = FALSE;
9468   static int xy[4][2] =
9469   {
9470     { 0, -1 },
9471     { -1, 0 },
9472     { +1, 0 },
9473     { 0, +1 }
9474   };
9475
9476   for (i = 0; i < NUM_DIRECTIONS; i++)
9477   {
9478     for (j = 0; j < 4; j++)
9479     {
9480       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9481
9482       if (IN_LEV_FIELD(xx, yy) &&
9483           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9484       {
9485         if (Feld[xx][yy] == EL_DRAGON)
9486           dragon_found = TRUE;
9487       }
9488       else
9489         break;
9490     }
9491   }
9492
9493   if (!dragon_found)
9494   {
9495     for (i = 0; i < NUM_DIRECTIONS; i++)
9496     {
9497       for (j = 0; j < 3; j++)
9498       {
9499         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9500   
9501         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9502         {
9503           Feld[xx][yy] = EL_EMPTY;
9504           TEST_DrawLevelField(xx, yy);
9505         }
9506         else
9507           break;
9508       }
9509     }
9510   }
9511 }
9512
9513 static void InitBuggyBase(int x, int y)
9514 {
9515   int element = Feld[x][y];
9516   int activating_delay = FRAMES_PER_SECOND / 4;
9517
9518   ChangeDelay[x][y] =
9519     (element == EL_SP_BUGGY_BASE ?
9520      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9521      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9522      activating_delay :
9523      element == EL_SP_BUGGY_BASE_ACTIVE ?
9524      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9525 }
9526
9527 static void WarnBuggyBase(int x, int y)
9528 {
9529   int i;
9530   static int xy[4][2] =
9531   {
9532     { 0, -1 },
9533     { -1, 0 },
9534     { +1, 0 },
9535     { 0, +1 }
9536   };
9537
9538   for (i = 0; i < NUM_DIRECTIONS; i++)
9539   {
9540     int xx = x + xy[i][0];
9541     int yy = y + xy[i][1];
9542
9543     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9544     {
9545       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9546
9547       break;
9548     }
9549   }
9550 }
9551
9552 static void InitTrap(int x, int y)
9553 {
9554   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9555 }
9556
9557 static void ActivateTrap(int x, int y)
9558 {
9559   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9560 }
9561
9562 static void ChangeActiveTrap(int x, int y)
9563 {
9564   int graphic = IMG_TRAP_ACTIVE;
9565
9566   /* if new animation frame was drawn, correct crumbled sand border */
9567   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9568     TEST_DrawLevelFieldCrumbled(x, y);
9569 }
9570
9571 static int getSpecialActionElement(int element, int number, int base_element)
9572 {
9573   return (element != EL_EMPTY ? element :
9574           number != -1 ? base_element + number - 1 :
9575           EL_EMPTY);
9576 }
9577
9578 static int getModifiedActionNumber(int value_old, int operator, int operand,
9579                                    int value_min, int value_max)
9580 {
9581   int value_new = (operator == CA_MODE_SET      ? operand :
9582                    operator == CA_MODE_ADD      ? value_old + operand :
9583                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9584                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9585                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9586                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9587                    value_old);
9588
9589   return (value_new < value_min ? value_min :
9590           value_new > value_max ? value_max :
9591           value_new);
9592 }
9593
9594 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9595 {
9596   struct ElementInfo *ei = &element_info[element];
9597   struct ElementChangeInfo *change = &ei->change_page[page];
9598   int target_element = change->target_element;
9599   int action_type = change->action_type;
9600   int action_mode = change->action_mode;
9601   int action_arg = change->action_arg;
9602   int action_element = change->action_element;
9603   int i;
9604
9605   if (!change->has_action)
9606     return;
9607
9608   /* ---------- determine action paramater values -------------------------- */
9609
9610   int level_time_value =
9611     (level.time > 0 ? TimeLeft :
9612      TimePlayed);
9613
9614   int action_arg_element_raw =
9615     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9616      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9617      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9618      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9619      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9620      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9621      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9622      EL_EMPTY);
9623   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9624
9625   int action_arg_direction =
9626     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9627      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9628      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9629      change->actual_trigger_side :
9630      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9631      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9632      MV_NONE);
9633
9634   int action_arg_number_min =
9635     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9636      CA_ARG_MIN);
9637
9638   int action_arg_number_max =
9639     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9640      action_type == CA_SET_LEVEL_GEMS ? 999 :
9641      action_type == CA_SET_LEVEL_TIME ? 9999 :
9642      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9643      action_type == CA_SET_CE_VALUE ? 9999 :
9644      action_type == CA_SET_CE_SCORE ? 9999 :
9645      CA_ARG_MAX);
9646
9647   int action_arg_number_reset =
9648     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9649      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9650      action_type == CA_SET_LEVEL_TIME ? level.time :
9651      action_type == CA_SET_LEVEL_SCORE ? 0 :
9652      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9653      action_type == CA_SET_CE_SCORE ? 0 :
9654      0);
9655
9656   int action_arg_number =
9657     (action_arg <= CA_ARG_MAX ? action_arg :
9658      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9659      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9660      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9661      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9662      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9663      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9664      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9665      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9666      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9667      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9668      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9669      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9670      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9671      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9672      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9673      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9674      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9675      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9676      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9677      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9678      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9679      -1);
9680
9681   int action_arg_number_old =
9682     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9683      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9684      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9685      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9686      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9687      0);
9688
9689   int action_arg_number_new =
9690     getModifiedActionNumber(action_arg_number_old,
9691                             action_mode, action_arg_number,
9692                             action_arg_number_min, action_arg_number_max);
9693
9694   int trigger_player_bits =
9695     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9696      change->actual_trigger_player_bits : change->trigger_player);
9697
9698   int action_arg_player_bits =
9699     (action_arg >= CA_ARG_PLAYER_1 &&
9700      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9701      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9702      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9703      PLAYER_BITS_ANY);
9704
9705   /* ---------- execute action  -------------------------------------------- */
9706
9707   switch (action_type)
9708   {
9709     case CA_NO_ACTION:
9710     {
9711       return;
9712     }
9713
9714     /* ---------- level actions  ------------------------------------------- */
9715
9716     case CA_RESTART_LEVEL:
9717     {
9718       game.restart_level = TRUE;
9719
9720       break;
9721     }
9722
9723     case CA_SHOW_ENVELOPE:
9724     {
9725       int element = getSpecialActionElement(action_arg_element,
9726                                             action_arg_number, EL_ENVELOPE_1);
9727
9728       if (IS_ENVELOPE(element))
9729         local_player->show_envelope = element;
9730
9731       break;
9732     }
9733
9734     case CA_SET_LEVEL_TIME:
9735     {
9736       if (level.time > 0)       /* only modify limited time value */
9737       {
9738         TimeLeft = action_arg_number_new;
9739
9740         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9741
9742         DisplayGameControlValues();
9743
9744         if (!TimeLeft && setup.time_limit)
9745           for (i = 0; i < MAX_PLAYERS; i++)
9746             KillPlayer(&stored_player[i]);
9747       }
9748
9749       break;
9750     }
9751
9752     case CA_SET_LEVEL_SCORE:
9753     {
9754       local_player->score = action_arg_number_new;
9755
9756       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9757
9758       DisplayGameControlValues();
9759
9760       break;
9761     }
9762
9763     case CA_SET_LEVEL_GEMS:
9764     {
9765       local_player->gems_still_needed = action_arg_number_new;
9766
9767       game.snapshot.collected_item = TRUE;
9768
9769       game_panel_controls[GAME_PANEL_GEMS].value =
9770         local_player->gems_still_needed;
9771
9772       DisplayGameControlValues();
9773
9774       break;
9775     }
9776
9777     case CA_SET_LEVEL_WIND:
9778     {
9779       game.wind_direction = action_arg_direction;
9780
9781       break;
9782     }
9783
9784     case CA_SET_LEVEL_RANDOM_SEED:
9785     {
9786       /* ensure that setting a new random seed while playing is predictable */
9787       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9788
9789       break;
9790     }
9791
9792     /* ---------- player actions  ------------------------------------------ */
9793
9794     case CA_MOVE_PLAYER:
9795     {
9796       /* automatically move to the next field in specified direction */
9797       for (i = 0; i < MAX_PLAYERS; i++)
9798         if (trigger_player_bits & (1 << i))
9799           stored_player[i].programmed_action = action_arg_direction;
9800
9801       break;
9802     }
9803
9804     case CA_EXIT_PLAYER:
9805     {
9806       for (i = 0; i < MAX_PLAYERS; i++)
9807         if (action_arg_player_bits & (1 << i))
9808           PlayerWins(&stored_player[i]);
9809
9810       break;
9811     }
9812
9813     case CA_KILL_PLAYER:
9814     {
9815       for (i = 0; i < MAX_PLAYERS; i++)
9816         if (action_arg_player_bits & (1 << i))
9817           KillPlayer(&stored_player[i]);
9818
9819       break;
9820     }
9821
9822     case CA_SET_PLAYER_KEYS:
9823     {
9824       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9825       int element = getSpecialActionElement(action_arg_element,
9826                                             action_arg_number, EL_KEY_1);
9827
9828       if (IS_KEY(element))
9829       {
9830         for (i = 0; i < MAX_PLAYERS; i++)
9831         {
9832           if (trigger_player_bits & (1 << i))
9833           {
9834             stored_player[i].key[KEY_NR(element)] = key_state;
9835
9836             DrawGameDoorValues();
9837           }
9838         }
9839       }
9840
9841       break;
9842     }
9843
9844     case CA_SET_PLAYER_SPEED:
9845     {
9846       for (i = 0; i < MAX_PLAYERS; i++)
9847       {
9848         if (trigger_player_bits & (1 << i))
9849         {
9850           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9851
9852           if (action_arg == CA_ARG_SPEED_FASTER &&
9853               stored_player[i].cannot_move)
9854           {
9855             action_arg_number = STEPSIZE_VERY_SLOW;
9856           }
9857           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9858                    action_arg == CA_ARG_SPEED_FASTER)
9859           {
9860             action_arg_number = 2;
9861             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9862                            CA_MODE_MULTIPLY);
9863           }
9864           else if (action_arg == CA_ARG_NUMBER_RESET)
9865           {
9866             action_arg_number = level.initial_player_stepsize[i];
9867           }
9868
9869           move_stepsize =
9870             getModifiedActionNumber(move_stepsize,
9871                                     action_mode,
9872                                     action_arg_number,
9873                                     action_arg_number_min,
9874                                     action_arg_number_max);
9875
9876           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9877         }
9878       }
9879
9880       break;
9881     }
9882
9883     case CA_SET_PLAYER_SHIELD:
9884     {
9885       for (i = 0; i < MAX_PLAYERS; i++)
9886       {
9887         if (trigger_player_bits & (1 << i))
9888         {
9889           if (action_arg == CA_ARG_SHIELD_OFF)
9890           {
9891             stored_player[i].shield_normal_time_left = 0;
9892             stored_player[i].shield_deadly_time_left = 0;
9893           }
9894           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9895           {
9896             stored_player[i].shield_normal_time_left = 999999;
9897           }
9898           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9899           {
9900             stored_player[i].shield_normal_time_left = 999999;
9901             stored_player[i].shield_deadly_time_left = 999999;
9902           }
9903         }
9904       }
9905
9906       break;
9907     }
9908
9909     case CA_SET_PLAYER_GRAVITY:
9910     {
9911       for (i = 0; i < MAX_PLAYERS; i++)
9912       {
9913         if (trigger_player_bits & (1 << i))
9914         {
9915           stored_player[i].gravity =
9916             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9917              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9918              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9919              stored_player[i].gravity);
9920         }
9921       }
9922
9923       break;
9924     }
9925
9926     case CA_SET_PLAYER_ARTWORK:
9927     {
9928       for (i = 0; i < MAX_PLAYERS; i++)
9929       {
9930         if (trigger_player_bits & (1 << i))
9931         {
9932           int artwork_element = action_arg_element;
9933
9934           if (action_arg == CA_ARG_ELEMENT_RESET)
9935             artwork_element =
9936               (level.use_artwork_element[i] ? level.artwork_element[i] :
9937                stored_player[i].element_nr);
9938
9939           if (stored_player[i].artwork_element != artwork_element)
9940             stored_player[i].Frame = 0;
9941
9942           stored_player[i].artwork_element = artwork_element;
9943
9944           SetPlayerWaiting(&stored_player[i], FALSE);
9945
9946           /* set number of special actions for bored and sleeping animation */
9947           stored_player[i].num_special_action_bored =
9948             get_num_special_action(artwork_element,
9949                                    ACTION_BORING_1, ACTION_BORING_LAST);
9950           stored_player[i].num_special_action_sleeping =
9951             get_num_special_action(artwork_element,
9952                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9953         }
9954       }
9955
9956       break;
9957     }
9958
9959     case CA_SET_PLAYER_INVENTORY:
9960     {
9961       for (i = 0; i < MAX_PLAYERS; i++)
9962       {
9963         struct PlayerInfo *player = &stored_player[i];
9964         int j, k;
9965
9966         if (trigger_player_bits & (1 << i))
9967         {
9968           int inventory_element = action_arg_element;
9969
9970           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9971               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9972               action_arg == CA_ARG_ELEMENT_ACTION)
9973           {
9974             int element = inventory_element;
9975             int collect_count = element_info[element].collect_count_initial;
9976
9977             if (!IS_CUSTOM_ELEMENT(element))
9978               collect_count = 1;
9979
9980             if (collect_count == 0)
9981               player->inventory_infinite_element = element;
9982             else
9983               for (k = 0; k < collect_count; k++)
9984                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9985                   player->inventory_element[player->inventory_size++] =
9986                     element;
9987           }
9988           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9989                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9990                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9991           {
9992             if (player->inventory_infinite_element != EL_UNDEFINED &&
9993                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9994                                      action_arg_element_raw))
9995               player->inventory_infinite_element = EL_UNDEFINED;
9996
9997             for (k = 0, j = 0; j < player->inventory_size; j++)
9998             {
9999               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10000                                         action_arg_element_raw))
10001                 player->inventory_element[k++] = player->inventory_element[j];
10002             }
10003
10004             player->inventory_size = k;
10005           }
10006           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10007           {
10008             if (player->inventory_size > 0)
10009             {
10010               for (j = 0; j < player->inventory_size - 1; j++)
10011                 player->inventory_element[j] = player->inventory_element[j + 1];
10012
10013               player->inventory_size--;
10014             }
10015           }
10016           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10017           {
10018             if (player->inventory_size > 0)
10019               player->inventory_size--;
10020           }
10021           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10022           {
10023             player->inventory_infinite_element = EL_UNDEFINED;
10024             player->inventory_size = 0;
10025           }
10026           else if (action_arg == CA_ARG_INVENTORY_RESET)
10027           {
10028             player->inventory_infinite_element = EL_UNDEFINED;
10029             player->inventory_size = 0;
10030
10031             if (level.use_initial_inventory[i])
10032             {
10033               for (j = 0; j < level.initial_inventory_size[i]; j++)
10034               {
10035                 int element = level.initial_inventory_content[i][j];
10036                 int collect_count = element_info[element].collect_count_initial;
10037
10038                 if (!IS_CUSTOM_ELEMENT(element))
10039                   collect_count = 1;
10040
10041                 if (collect_count == 0)
10042                   player->inventory_infinite_element = element;
10043                 else
10044                   for (k = 0; k < collect_count; k++)
10045                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10046                       player->inventory_element[player->inventory_size++] =
10047                         element;
10048               }
10049             }
10050           }
10051         }
10052       }
10053
10054       break;
10055     }
10056
10057     /* ---------- CE actions  ---------------------------------------------- */
10058
10059     case CA_SET_CE_VALUE:
10060     {
10061       int last_ce_value = CustomValue[x][y];
10062
10063       CustomValue[x][y] = action_arg_number_new;
10064
10065       if (CustomValue[x][y] != last_ce_value)
10066       {
10067         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10068         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10069
10070         if (CustomValue[x][y] == 0)
10071         {
10072           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10073           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10074         }
10075       }
10076
10077       break;
10078     }
10079
10080     case CA_SET_CE_SCORE:
10081     {
10082       int last_ce_score = ei->collect_score;
10083
10084       ei->collect_score = action_arg_number_new;
10085
10086       if (ei->collect_score != last_ce_score)
10087       {
10088         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10089         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10090
10091         if (ei->collect_score == 0)
10092         {
10093           int xx, yy;
10094
10095           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10096           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10097
10098           /*
10099             This is a very special case that seems to be a mixture between
10100             CheckElementChange() and CheckTriggeredElementChange(): while
10101             the first one only affects single elements that are triggered
10102             directly, the second one affects multiple elements in the playfield
10103             that are triggered indirectly by another element. This is a third
10104             case: Changing the CE score always affects multiple identical CEs,
10105             so every affected CE must be checked, not only the single CE for
10106             which the CE score was changed in the first place (as every instance
10107             of that CE shares the same CE score, and therefore also can change)!
10108           */
10109           SCAN_PLAYFIELD(xx, yy)
10110           {
10111             if (Feld[xx][yy] == element)
10112               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10113                                  CE_SCORE_GETS_ZERO);
10114           }
10115         }
10116       }
10117
10118       break;
10119     }
10120
10121     case CA_SET_CE_ARTWORK:
10122     {
10123       int artwork_element = action_arg_element;
10124       boolean reset_frame = FALSE;
10125       int xx, yy;
10126
10127       if (action_arg == CA_ARG_ELEMENT_RESET)
10128         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10129                            element);
10130
10131       if (ei->gfx_element != artwork_element)
10132         reset_frame = TRUE;
10133
10134       ei->gfx_element = artwork_element;
10135
10136       SCAN_PLAYFIELD(xx, yy)
10137       {
10138         if (Feld[xx][yy] == element)
10139         {
10140           if (reset_frame)
10141           {
10142             ResetGfxAnimation(xx, yy);
10143             ResetRandomAnimationValue(xx, yy);
10144           }
10145
10146           TEST_DrawLevelField(xx, yy);
10147         }
10148       }
10149
10150       break;
10151     }
10152
10153     /* ---------- engine actions  ------------------------------------------ */
10154
10155     case CA_SET_ENGINE_SCAN_MODE:
10156     {
10157       InitPlayfieldScanMode(action_arg);
10158
10159       break;
10160     }
10161
10162     default:
10163       break;
10164   }
10165 }
10166
10167 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10168 {
10169   int old_element = Feld[x][y];
10170   int new_element = GetElementFromGroupElement(element);
10171   int previous_move_direction = MovDir[x][y];
10172   int last_ce_value = CustomValue[x][y];
10173   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10174   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10175   boolean add_player_onto_element = (new_element_is_player &&
10176                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10177                                      IS_WALKABLE(old_element));
10178
10179   if (!add_player_onto_element)
10180   {
10181     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10182       RemoveMovingField(x, y);
10183     else
10184       RemoveField(x, y);
10185
10186     Feld[x][y] = new_element;
10187
10188     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10189       MovDir[x][y] = previous_move_direction;
10190
10191     if (element_info[new_element].use_last_ce_value)
10192       CustomValue[x][y] = last_ce_value;
10193
10194     InitField_WithBug1(x, y, FALSE);
10195
10196     new_element = Feld[x][y];   /* element may have changed */
10197
10198     ResetGfxAnimation(x, y);
10199     ResetRandomAnimationValue(x, y);
10200
10201     TEST_DrawLevelField(x, y);
10202
10203     if (GFX_CRUMBLED(new_element))
10204       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10205   }
10206
10207   /* check if element under the player changes from accessible to unaccessible
10208      (needed for special case of dropping element which then changes) */
10209   /* (must be checked after creating new element for walkable group elements) */
10210   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10211       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10212   {
10213     Bang(x, y);
10214
10215     return;
10216   }
10217
10218   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10219   if (new_element_is_player)
10220     RelocatePlayer(x, y, new_element);
10221
10222   if (is_change)
10223     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10224
10225   TestIfBadThingTouchesPlayer(x, y);
10226   TestIfPlayerTouchesCustomElement(x, y);
10227   TestIfElementTouchesCustomElement(x, y);
10228 }
10229
10230 static void CreateField(int x, int y, int element)
10231 {
10232   CreateFieldExt(x, y, element, FALSE);
10233 }
10234
10235 static void CreateElementFromChange(int x, int y, int element)
10236 {
10237   element = GET_VALID_RUNTIME_ELEMENT(element);
10238
10239   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10240   {
10241     int old_element = Feld[x][y];
10242
10243     /* prevent changed element from moving in same engine frame
10244        unless both old and new element can either fall or move */
10245     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10246         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10247       Stop[x][y] = TRUE;
10248   }
10249
10250   CreateFieldExt(x, y, element, TRUE);
10251 }
10252
10253 static boolean ChangeElement(int x, int y, int element, int page)
10254 {
10255   struct ElementInfo *ei = &element_info[element];
10256   struct ElementChangeInfo *change = &ei->change_page[page];
10257   int ce_value = CustomValue[x][y];
10258   int ce_score = ei->collect_score;
10259   int target_element;
10260   int old_element = Feld[x][y];
10261
10262   /* always use default change event to prevent running into a loop */
10263   if (ChangeEvent[x][y] == -1)
10264     ChangeEvent[x][y] = CE_DELAY;
10265
10266   if (ChangeEvent[x][y] == CE_DELAY)
10267   {
10268     /* reset actual trigger element, trigger player and action element */
10269     change->actual_trigger_element = EL_EMPTY;
10270     change->actual_trigger_player = EL_EMPTY;
10271     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10272     change->actual_trigger_side = CH_SIDE_NONE;
10273     change->actual_trigger_ce_value = 0;
10274     change->actual_trigger_ce_score = 0;
10275   }
10276
10277   /* do not change elements more than a specified maximum number of changes */
10278   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10279     return FALSE;
10280
10281   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10282
10283   if (change->explode)
10284   {
10285     Bang(x, y);
10286
10287     return TRUE;
10288   }
10289
10290   if (change->use_target_content)
10291   {
10292     boolean complete_replace = TRUE;
10293     boolean can_replace[3][3];
10294     int xx, yy;
10295
10296     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10297     {
10298       boolean is_empty;
10299       boolean is_walkable;
10300       boolean is_diggable;
10301       boolean is_collectible;
10302       boolean is_removable;
10303       boolean is_destructible;
10304       int ex = x + xx - 1;
10305       int ey = y + yy - 1;
10306       int content_element = change->target_content.e[xx][yy];
10307       int e;
10308
10309       can_replace[xx][yy] = TRUE;
10310
10311       if (ex == x && ey == y)   /* do not check changing element itself */
10312         continue;
10313
10314       if (content_element == EL_EMPTY_SPACE)
10315       {
10316         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10317
10318         continue;
10319       }
10320
10321       if (!IN_LEV_FIELD(ex, ey))
10322       {
10323         can_replace[xx][yy] = FALSE;
10324         complete_replace = FALSE;
10325
10326         continue;
10327       }
10328
10329       e = Feld[ex][ey];
10330
10331       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10332         e = MovingOrBlocked2Element(ex, ey);
10333
10334       is_empty = (IS_FREE(ex, ey) ||
10335                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10336
10337       is_walkable     = (is_empty || IS_WALKABLE(e));
10338       is_diggable     = (is_empty || IS_DIGGABLE(e));
10339       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10340       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10341       is_removable    = (is_diggable || is_collectible);
10342
10343       can_replace[xx][yy] =
10344         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10345           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10346           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10347           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10348           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10349           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10350          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10351
10352       if (!can_replace[xx][yy])
10353         complete_replace = FALSE;
10354     }
10355
10356     if (!change->only_if_complete || complete_replace)
10357     {
10358       boolean something_has_changed = FALSE;
10359
10360       if (change->only_if_complete && change->use_random_replace &&
10361           RND(100) < change->random_percentage)
10362         return FALSE;
10363
10364       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10365       {
10366         int ex = x + xx - 1;
10367         int ey = y + yy - 1;
10368         int content_element;
10369
10370         if (can_replace[xx][yy] && (!change->use_random_replace ||
10371                                     RND(100) < change->random_percentage))
10372         {
10373           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10374             RemoveMovingField(ex, ey);
10375
10376           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10377
10378           content_element = change->target_content.e[xx][yy];
10379           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10380                                               ce_value, ce_score);
10381
10382           CreateElementFromChange(ex, ey, target_element);
10383
10384           something_has_changed = TRUE;
10385
10386           /* for symmetry reasons, freeze newly created border elements */
10387           if (ex != x || ey != y)
10388             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10389         }
10390       }
10391
10392       if (something_has_changed)
10393       {
10394         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10395         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10396       }
10397     }
10398   }
10399   else
10400   {
10401     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10402                                         ce_value, ce_score);
10403
10404     if (element == EL_DIAGONAL_GROWING ||
10405         element == EL_DIAGONAL_SHRINKING)
10406     {
10407       target_element = Store[x][y];
10408
10409       Store[x][y] = EL_EMPTY;
10410     }
10411
10412     CreateElementFromChange(x, y, target_element);
10413
10414     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10415     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10416   }
10417
10418   /* this uses direct change before indirect change */
10419   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10420
10421   return TRUE;
10422 }
10423
10424 static void HandleElementChange(int x, int y, int page)
10425 {
10426   int element = MovingOrBlocked2Element(x, y);
10427   struct ElementInfo *ei = &element_info[element];
10428   struct ElementChangeInfo *change = &ei->change_page[page];
10429   boolean handle_action_before_change = FALSE;
10430
10431 #ifdef DEBUG
10432   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10433       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10434   {
10435     printf("\n\n");
10436     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10437            x, y, element, element_info[element].token_name);
10438     printf("HandleElementChange(): This should never happen!\n");
10439     printf("\n\n");
10440   }
10441 #endif
10442
10443   /* this can happen with classic bombs on walkable, changing elements */
10444   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10445   {
10446     return;
10447   }
10448
10449   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10450   {
10451     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10452
10453     if (change->can_change)
10454     {
10455       /* !!! not clear why graphic animation should be reset at all here !!! */
10456       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10457       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10458
10459       /*
10460         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10461
10462         When using an animation frame delay of 1 (this only happens with
10463         "sp_zonk.moving.left/right" in the classic graphics), the default
10464         (non-moving) animation shows wrong animation frames (while the
10465         moving animation, like "sp_zonk.moving.left/right", is correct,
10466         so this graphical bug never shows up with the classic graphics).
10467         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10468         be drawn instead of the correct frames 0,1,2,3. This is caused by
10469         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10470         an element change: First when the change delay ("ChangeDelay[][]")
10471         counter has reached zero after decrementing, then a second time in
10472         the next frame (after "GfxFrame[][]" was already incremented) when
10473         "ChangeDelay[][]" is reset to the initial delay value again.
10474
10475         This causes frame 0 to be drawn twice, while the last frame won't
10476         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10477
10478         As some animations may already be cleverly designed around this bug
10479         (at least the "Snake Bite" snake tail animation does this), it cannot
10480         simply be fixed here without breaking such existing animations.
10481         Unfortunately, it cannot easily be detected if a graphics set was
10482         designed "before" or "after" the bug was fixed. As a workaround,
10483         a new graphics set option "game.graphics_engine_version" was added
10484         to be able to specify the game's major release version for which the
10485         graphics set was designed, which can then be used to decide if the
10486         bugfix should be used (version 4 and above) or not (version 3 or
10487         below, or if no version was specified at all, as with old sets).
10488
10489         (The wrong/fixed animation frames can be tested with the test level set
10490         "test_gfxframe" and level "000", which contains a specially prepared
10491         custom element at level position (x/y) == (11/9) which uses the zonk
10492         animation mentioned above. Using "game.graphics_engine_version: 4"
10493         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10494         This can also be seen from the debug output for this test element.)
10495       */
10496
10497       /* when a custom element is about to change (for example by change delay),
10498          do not reset graphic animation when the custom element is moving */
10499       if (game.graphics_engine_version < 4 &&
10500           !IS_MOVING(x, y))
10501       {
10502         ResetGfxAnimation(x, y);
10503         ResetRandomAnimationValue(x, y);
10504       }
10505
10506       if (change->pre_change_function)
10507         change->pre_change_function(x, y);
10508     }
10509   }
10510
10511   ChangeDelay[x][y]--;
10512
10513   if (ChangeDelay[x][y] != 0)           /* continue element change */
10514   {
10515     if (change->can_change)
10516     {
10517       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10518
10519       if (IS_ANIMATED(graphic))
10520         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10521
10522       if (change->change_function)
10523         change->change_function(x, y);
10524     }
10525   }
10526   else                                  /* finish element change */
10527   {
10528     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10529     {
10530       page = ChangePage[x][y];
10531       ChangePage[x][y] = -1;
10532
10533       change = &ei->change_page[page];
10534     }
10535
10536     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10537     {
10538       ChangeDelay[x][y] = 1;            /* try change after next move step */
10539       ChangePage[x][y] = page;          /* remember page to use for change */
10540
10541       return;
10542     }
10543
10544     /* special case: set new level random seed before changing element */
10545     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10546       handle_action_before_change = TRUE;
10547
10548     if (change->has_action && handle_action_before_change)
10549       ExecuteCustomElementAction(x, y, element, page);
10550
10551     if (change->can_change)
10552     {
10553       if (ChangeElement(x, y, element, page))
10554       {
10555         if (change->post_change_function)
10556           change->post_change_function(x, y);
10557       }
10558     }
10559
10560     if (change->has_action && !handle_action_before_change)
10561       ExecuteCustomElementAction(x, y, element, page);
10562   }
10563 }
10564
10565 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10566                                               int trigger_element,
10567                                               int trigger_event,
10568                                               int trigger_player,
10569                                               int trigger_side,
10570                                               int trigger_page)
10571 {
10572   boolean change_done_any = FALSE;
10573   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10574   int i;
10575
10576   if (!(trigger_events[trigger_element][trigger_event]))
10577     return FALSE;
10578
10579   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10580
10581   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10582   {
10583     int element = EL_CUSTOM_START + i;
10584     boolean change_done = FALSE;
10585     int p;
10586
10587     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10588         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10589       continue;
10590
10591     for (p = 0; p < element_info[element].num_change_pages; p++)
10592     {
10593       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10594
10595       if (change->can_change_or_has_action &&
10596           change->has_event[trigger_event] &&
10597           change->trigger_side & trigger_side &&
10598           change->trigger_player & trigger_player &&
10599           change->trigger_page & trigger_page_bits &&
10600           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10601       {
10602         change->actual_trigger_element = trigger_element;
10603         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10604         change->actual_trigger_player_bits = trigger_player;
10605         change->actual_trigger_side = trigger_side;
10606         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10607         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10608
10609         if ((change->can_change && !change_done) || change->has_action)
10610         {
10611           int x, y;
10612
10613           SCAN_PLAYFIELD(x, y)
10614           {
10615             if (Feld[x][y] == element)
10616             {
10617               if (change->can_change && !change_done)
10618               {
10619                 /* if element already changed in this frame, not only prevent
10620                    another element change (checked in ChangeElement()), but
10621                    also prevent additional element actions for this element */
10622
10623                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10624                     !level.use_action_after_change_bug)
10625                   continue;
10626
10627                 ChangeDelay[x][y] = 1;
10628                 ChangeEvent[x][y] = trigger_event;
10629
10630                 HandleElementChange(x, y, p);
10631               }
10632               else if (change->has_action)
10633               {
10634                 /* if element already changed in this frame, not only prevent
10635                    another element change (checked in ChangeElement()), but
10636                    also prevent additional element actions for this element */
10637
10638                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10639                     !level.use_action_after_change_bug)
10640                   continue;
10641
10642                 ExecuteCustomElementAction(x, y, element, p);
10643                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10644               }
10645             }
10646           }
10647
10648           if (change->can_change)
10649           {
10650             change_done = TRUE;
10651             change_done_any = TRUE;
10652           }
10653         }
10654       }
10655     }
10656   }
10657
10658   RECURSION_LOOP_DETECTION_END();
10659
10660   return change_done_any;
10661 }
10662
10663 static boolean CheckElementChangeExt(int x, int y,
10664                                      int element,
10665                                      int trigger_element,
10666                                      int trigger_event,
10667                                      int trigger_player,
10668                                      int trigger_side)
10669 {
10670   boolean change_done = FALSE;
10671   int p;
10672
10673   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10674       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10675     return FALSE;
10676
10677   if (Feld[x][y] == EL_BLOCKED)
10678   {
10679     Blocked2Moving(x, y, &x, &y);
10680     element = Feld[x][y];
10681   }
10682
10683   /* check if element has already changed or is about to change after moving */
10684   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10685        Feld[x][y] != element) ||
10686
10687       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10688        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10689         ChangePage[x][y] != -1)))
10690     return FALSE;
10691
10692   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10693
10694   for (p = 0; p < element_info[element].num_change_pages; p++)
10695   {
10696     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10697
10698     /* check trigger element for all events where the element that is checked
10699        for changing interacts with a directly adjacent element -- this is
10700        different to element changes that affect other elements to change on the
10701        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10702     boolean check_trigger_element =
10703       (trigger_event == CE_TOUCHING_X ||
10704        trigger_event == CE_HITTING_X ||
10705        trigger_event == CE_HIT_BY_X ||
10706        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10707
10708     if (change->can_change_or_has_action &&
10709         change->has_event[trigger_event] &&
10710         change->trigger_side & trigger_side &&
10711         change->trigger_player & trigger_player &&
10712         (!check_trigger_element ||
10713          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10714     {
10715       change->actual_trigger_element = trigger_element;
10716       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10717       change->actual_trigger_player_bits = trigger_player;
10718       change->actual_trigger_side = trigger_side;
10719       change->actual_trigger_ce_value = CustomValue[x][y];
10720       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10721
10722       /* special case: trigger element not at (x,y) position for some events */
10723       if (check_trigger_element)
10724       {
10725         static struct
10726         {
10727           int dx, dy;
10728         } move_xy[] =
10729           {
10730             {  0,  0 },
10731             { -1,  0 },
10732             { +1,  0 },
10733             {  0,  0 },
10734             {  0, -1 },
10735             {  0,  0 }, { 0, 0 }, { 0, 0 },
10736             {  0, +1 }
10737           };
10738
10739         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10740         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10741
10742         change->actual_trigger_ce_value = CustomValue[xx][yy];
10743         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10744       }
10745
10746       if (change->can_change && !change_done)
10747       {
10748         ChangeDelay[x][y] = 1;
10749         ChangeEvent[x][y] = trigger_event;
10750
10751         HandleElementChange(x, y, p);
10752
10753         change_done = TRUE;
10754       }
10755       else if (change->has_action)
10756       {
10757         ExecuteCustomElementAction(x, y, element, p);
10758         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10759       }
10760     }
10761   }
10762
10763   RECURSION_LOOP_DETECTION_END();
10764
10765   return change_done;
10766 }
10767
10768 static void PlayPlayerSound(struct PlayerInfo *player)
10769 {
10770   int jx = player->jx, jy = player->jy;
10771   int sound_element = player->artwork_element;
10772   int last_action = player->last_action_waiting;
10773   int action = player->action_waiting;
10774
10775   if (player->is_waiting)
10776   {
10777     if (action != last_action)
10778       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10779     else
10780       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10781   }
10782   else
10783   {
10784     if (action != last_action)
10785       StopSound(element_info[sound_element].sound[last_action]);
10786
10787     if (last_action == ACTION_SLEEPING)
10788       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10789   }
10790 }
10791
10792 static void PlayAllPlayersSound()
10793 {
10794   int i;
10795
10796   for (i = 0; i < MAX_PLAYERS; i++)
10797     if (stored_player[i].active)
10798       PlayPlayerSound(&stored_player[i]);
10799 }
10800
10801 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10802 {
10803   boolean last_waiting = player->is_waiting;
10804   int move_dir = player->MovDir;
10805
10806   player->dir_waiting = move_dir;
10807   player->last_action_waiting = player->action_waiting;
10808
10809   if (is_waiting)
10810   {
10811     if (!last_waiting)          /* not waiting -> waiting */
10812     {
10813       player->is_waiting = TRUE;
10814
10815       player->frame_counter_bored =
10816         FrameCounter +
10817         game.player_boring_delay_fixed +
10818         GetSimpleRandom(game.player_boring_delay_random);
10819       player->frame_counter_sleeping =
10820         FrameCounter +
10821         game.player_sleeping_delay_fixed +
10822         GetSimpleRandom(game.player_sleeping_delay_random);
10823
10824       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10825     }
10826
10827     if (game.player_sleeping_delay_fixed +
10828         game.player_sleeping_delay_random > 0 &&
10829         player->anim_delay_counter == 0 &&
10830         player->post_delay_counter == 0 &&
10831         FrameCounter >= player->frame_counter_sleeping)
10832       player->is_sleeping = TRUE;
10833     else if (game.player_boring_delay_fixed +
10834              game.player_boring_delay_random > 0 &&
10835              FrameCounter >= player->frame_counter_bored)
10836       player->is_bored = TRUE;
10837
10838     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10839                               player->is_bored ? ACTION_BORING :
10840                               ACTION_WAITING);
10841
10842     if (player->is_sleeping && player->use_murphy)
10843     {
10844       /* special case for sleeping Murphy when leaning against non-free tile */
10845
10846       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10847           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10848            !IS_MOVING(player->jx - 1, player->jy)))
10849         move_dir = MV_LEFT;
10850       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10851                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10852                 !IS_MOVING(player->jx + 1, player->jy)))
10853         move_dir = MV_RIGHT;
10854       else
10855         player->is_sleeping = FALSE;
10856
10857       player->dir_waiting = move_dir;
10858     }
10859
10860     if (player->is_sleeping)
10861     {
10862       if (player->num_special_action_sleeping > 0)
10863       {
10864         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10865         {
10866           int last_special_action = player->special_action_sleeping;
10867           int num_special_action = player->num_special_action_sleeping;
10868           int special_action =
10869             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10870              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10871              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10872              last_special_action + 1 : ACTION_SLEEPING);
10873           int special_graphic =
10874             el_act_dir2img(player->artwork_element, special_action, move_dir);
10875
10876           player->anim_delay_counter =
10877             graphic_info[special_graphic].anim_delay_fixed +
10878             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10879           player->post_delay_counter =
10880             graphic_info[special_graphic].post_delay_fixed +
10881             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10882
10883           player->special_action_sleeping = special_action;
10884         }
10885
10886         if (player->anim_delay_counter > 0)
10887         {
10888           player->action_waiting = player->special_action_sleeping;
10889           player->anim_delay_counter--;
10890         }
10891         else if (player->post_delay_counter > 0)
10892         {
10893           player->post_delay_counter--;
10894         }
10895       }
10896     }
10897     else if (player->is_bored)
10898     {
10899       if (player->num_special_action_bored > 0)
10900       {
10901         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10902         {
10903           int special_action =
10904             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10905           int special_graphic =
10906             el_act_dir2img(player->artwork_element, special_action, move_dir);
10907
10908           player->anim_delay_counter =
10909             graphic_info[special_graphic].anim_delay_fixed +
10910             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10911           player->post_delay_counter =
10912             graphic_info[special_graphic].post_delay_fixed +
10913             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10914
10915           player->special_action_bored = special_action;
10916         }
10917
10918         if (player->anim_delay_counter > 0)
10919         {
10920           player->action_waiting = player->special_action_bored;
10921           player->anim_delay_counter--;
10922         }
10923         else if (player->post_delay_counter > 0)
10924         {
10925           player->post_delay_counter--;
10926         }
10927       }
10928     }
10929   }
10930   else if (last_waiting)        /* waiting -> not waiting */
10931   {
10932     player->is_waiting = FALSE;
10933     player->is_bored = FALSE;
10934     player->is_sleeping = FALSE;
10935
10936     player->frame_counter_bored = -1;
10937     player->frame_counter_sleeping = -1;
10938
10939     player->anim_delay_counter = 0;
10940     player->post_delay_counter = 0;
10941
10942     player->dir_waiting = player->MovDir;
10943     player->action_waiting = ACTION_DEFAULT;
10944
10945     player->special_action_bored = ACTION_DEFAULT;
10946     player->special_action_sleeping = ACTION_DEFAULT;
10947   }
10948 }
10949
10950 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10951 {
10952   if ((!player->is_moving  && player->was_moving) ||
10953       (player->MovPos == 0 && player->was_moving) ||
10954       (player->is_snapping && !player->was_snapping) ||
10955       (player->is_dropping && !player->was_dropping))
10956   {
10957     if (!CheckSaveEngineSnapshotToList())
10958       return;
10959
10960     player->was_moving = FALSE;
10961     player->was_snapping = TRUE;
10962     player->was_dropping = TRUE;
10963   }
10964   else
10965   {
10966     if (player->is_moving)
10967       player->was_moving = TRUE;
10968
10969     if (!player->is_snapping)
10970       player->was_snapping = FALSE;
10971
10972     if (!player->is_dropping)
10973       player->was_dropping = FALSE;
10974   }
10975 }
10976
10977 static void CheckSingleStepMode(struct PlayerInfo *player)
10978 {
10979   if (tape.single_step && tape.recording && !tape.pausing)
10980   {
10981     /* as it is called "single step mode", just return to pause mode when the
10982        player stopped moving after one tile (or never starts moving at all) */
10983     if (!player->is_moving &&
10984         !player->is_pushing &&
10985         !player->is_dropping_pressed)
10986     {
10987       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10988       SnapField(player, 0, 0);                  /* stop snapping */
10989     }
10990   }
10991
10992   CheckSaveEngineSnapshot(player);
10993 }
10994
10995 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10996 {
10997   int left      = player_action & JOY_LEFT;
10998   int right     = player_action & JOY_RIGHT;
10999   int up        = player_action & JOY_UP;
11000   int down      = player_action & JOY_DOWN;
11001   int button1   = player_action & JOY_BUTTON_1;
11002   int button2   = player_action & JOY_BUTTON_2;
11003   int dx        = (left ? -1 : right ? 1 : 0);
11004   int dy        = (up   ? -1 : down  ? 1 : 0);
11005
11006   if (!player->active || tape.pausing)
11007     return 0;
11008
11009   if (player_action)
11010   {
11011     if (button1)
11012       SnapField(player, dx, dy);
11013     else
11014     {
11015       if (button2)
11016         DropElement(player);
11017
11018       MovePlayer(player, dx, dy);
11019     }
11020
11021     CheckSingleStepMode(player);
11022
11023     SetPlayerWaiting(player, FALSE);
11024
11025     return player_action;
11026   }
11027   else
11028   {
11029     /* no actions for this player (no input at player's configured device) */
11030
11031     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11032     SnapField(player, 0, 0);
11033     CheckGravityMovementWhenNotMoving(player);
11034
11035     if (player->MovPos == 0)
11036       SetPlayerWaiting(player, TRUE);
11037
11038     if (player->MovPos == 0)    /* needed for tape.playing */
11039       player->is_moving = FALSE;
11040
11041     player->is_dropping = FALSE;
11042     player->is_dropping_pressed = FALSE;
11043     player->drop_pressed_delay = 0;
11044
11045     CheckSingleStepMode(player);
11046
11047     return 0;
11048   }
11049 }
11050
11051 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11052                                          byte *tape_action)
11053 {
11054   if (!tape.use_mouse)
11055     return;
11056
11057   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11058   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11059   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11060 }
11061
11062 static void SetTapeActionFromMouseAction(byte *tape_action,
11063                                          struct MouseActionInfo *mouse_action)
11064 {
11065   if (!tape.use_mouse)
11066     return;
11067
11068   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11069   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11070   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11071 }
11072
11073 static void CheckLevelTime()
11074 {
11075   int i;
11076
11077   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11078   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11079   {
11080     if (level.native_em_level->lev->home == 0)  /* all players at home */
11081     {
11082       PlayerWins(local_player);
11083
11084       AllPlayersGone = TRUE;
11085
11086       level.native_em_level->lev->home = -1;
11087     }
11088
11089     if (level.native_em_level->ply[0]->alive == 0 &&
11090         level.native_em_level->ply[1]->alive == 0 &&
11091         level.native_em_level->ply[2]->alive == 0 &&
11092         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11093       AllPlayersGone = TRUE;
11094   }
11095   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11096   {
11097     if (game_sp.LevelSolved &&
11098         !game_sp.GameOver)                              /* game won */
11099     {
11100       PlayerWins(local_player);
11101
11102       game_sp.GameOver = TRUE;
11103
11104       AllPlayersGone = TRUE;
11105     }
11106
11107     if (game_sp.GameOver)                               /* game lost */
11108       AllPlayersGone = TRUE;
11109   }
11110   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11111   {
11112     if (game_mm.level_solved &&
11113         !game_mm.game_over)                             /* game won */
11114     {
11115       PlayerWins(local_player);
11116
11117       game_mm.game_over = TRUE;
11118
11119       AllPlayersGone = TRUE;
11120     }
11121
11122     if (game_mm.game_over)                              /* game lost */
11123       AllPlayersGone = TRUE;
11124   }
11125
11126   if (TimeFrames >= FRAMES_PER_SECOND)
11127   {
11128     TimeFrames = 0;
11129     TapeTime++;
11130
11131     for (i = 0; i < MAX_PLAYERS; i++)
11132     {
11133       struct PlayerInfo *player = &stored_player[i];
11134
11135       if (SHIELD_ON(player))
11136       {
11137         player->shield_normal_time_left--;
11138
11139         if (player->shield_deadly_time_left > 0)
11140           player->shield_deadly_time_left--;
11141       }
11142     }
11143
11144     if (!local_player->LevelSolved && !level.use_step_counter)
11145     {
11146       TimePlayed++;
11147
11148       if (TimeLeft > 0)
11149       {
11150         TimeLeft--;
11151
11152         if (TimeLeft <= 10 && setup.time_limit)
11153           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11154
11155         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11156            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11157
11158         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11159
11160         if (!TimeLeft && setup.time_limit)
11161         {
11162           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11163             level.native_em_level->lev->killed_out_of_time = TRUE;
11164           else
11165             for (i = 0; i < MAX_PLAYERS; i++)
11166               KillPlayer(&stored_player[i]);
11167         }
11168       }
11169       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
11170       {
11171         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11172       }
11173
11174       level.native_em_level->lev->time =
11175         (game.no_time_limit ? TimePlayed : TimeLeft);
11176     }
11177
11178     if (tape.recording || tape.playing)
11179       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11180   }
11181
11182   if (tape.recording || tape.playing)
11183     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11184
11185   UpdateAndDisplayGameControlValues();
11186 }
11187
11188 void AdvanceFrameAndPlayerCounters(int player_nr)
11189 {
11190   int i;
11191
11192   /* advance frame counters (global frame counter and time frame counter) */
11193   FrameCounter++;
11194   TimeFrames++;
11195
11196   /* advance player counters (counters for move delay, move animation etc.) */
11197   for (i = 0; i < MAX_PLAYERS; i++)
11198   {
11199     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11200     int move_delay_value = stored_player[i].move_delay_value;
11201     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11202
11203     if (!advance_player_counters)       /* not all players may be affected */
11204       continue;
11205
11206     if (move_frames == 0)       /* less than one move per game frame */
11207     {
11208       int stepsize = TILEX / move_delay_value;
11209       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11210       int count = (stored_player[i].is_moving ?
11211                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11212
11213       if (count % delay == 0)
11214         move_frames = 1;
11215     }
11216
11217     stored_player[i].Frame += move_frames;
11218
11219     if (stored_player[i].MovPos != 0)
11220       stored_player[i].StepFrame += move_frames;
11221
11222     if (stored_player[i].move_delay > 0)
11223       stored_player[i].move_delay--;
11224
11225     /* due to bugs in previous versions, counter must count up, not down */
11226     if (stored_player[i].push_delay != -1)
11227       stored_player[i].push_delay++;
11228
11229     if (stored_player[i].drop_delay > 0)
11230       stored_player[i].drop_delay--;
11231
11232     if (stored_player[i].is_dropping_pressed)
11233       stored_player[i].drop_pressed_delay++;
11234   }
11235 }
11236
11237 void StartGameActions(boolean init_network_game, boolean record_tape,
11238                       int random_seed)
11239 {
11240   unsigned int new_random_seed = InitRND(random_seed);
11241
11242   if (record_tape)
11243     TapeStartRecording(new_random_seed);
11244
11245 #if defined(NETWORK_AVALIABLE)
11246   if (init_network_game)
11247   {
11248     SendToServer_StartPlaying();
11249
11250     return;
11251   }
11252 #endif
11253
11254   InitGame();
11255 }
11256
11257 void GameActionsExt()
11258 {
11259 #if 0
11260   static unsigned int game_frame_delay = 0;
11261 #endif
11262   unsigned int game_frame_delay_value;
11263   byte *recorded_player_action;
11264   byte summarized_player_action = 0;
11265   byte tape_action[MAX_PLAYERS];
11266   int i;
11267
11268   /* detect endless loops, caused by custom element programming */
11269   if (recursion_loop_detected && recursion_loop_depth == 0)
11270   {
11271     char *message = getStringCat3("Internal Error! Element ",
11272                                   EL_NAME(recursion_loop_element),
11273                                   " caused endless loop! Quit the game?");
11274
11275     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11276           EL_NAME(recursion_loop_element));
11277
11278     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11279
11280     recursion_loop_detected = FALSE;    /* if game should be continued */
11281
11282     free(message);
11283
11284     return;
11285   }
11286
11287   if (game.restart_level)
11288     StartGameActions(options.network, setup.autorecord, level.random_seed);
11289
11290   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11291   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11292   {
11293     if (level.native_em_level->lev->home == 0)  /* all players at home */
11294     {
11295       PlayerWins(local_player);
11296
11297       AllPlayersGone = TRUE;
11298
11299       level.native_em_level->lev->home = -1;
11300     }
11301
11302     if (level.native_em_level->ply[0]->alive == 0 &&
11303         level.native_em_level->ply[1]->alive == 0 &&
11304         level.native_em_level->ply[2]->alive == 0 &&
11305         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11306       AllPlayersGone = TRUE;
11307   }
11308   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11309   {
11310     if (game_sp.LevelSolved &&
11311         !game_sp.GameOver)                              /* game won */
11312     {
11313       PlayerWins(local_player);
11314
11315       game_sp.GameOver = TRUE;
11316
11317       AllPlayersGone = TRUE;
11318     }
11319
11320     if (game_sp.GameOver)                               /* game lost */
11321       AllPlayersGone = TRUE;
11322   }
11323   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11324   {
11325     if (game_mm.level_solved &&
11326         !game_mm.game_over)                             /* game won */
11327     {
11328       PlayerWins(local_player);
11329
11330       game_mm.game_over = TRUE;
11331
11332       AllPlayersGone = TRUE;
11333     }
11334
11335     if (game_mm.game_over)                              /* game lost */
11336       AllPlayersGone = TRUE;
11337   }
11338
11339   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11340     GameWon();
11341
11342   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11343     TapeStop();
11344
11345   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11346     return;
11347
11348   game_frame_delay_value =
11349     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11350
11351   if (tape.playing && tape.warp_forward && !tape.pausing)
11352     game_frame_delay_value = 0;
11353
11354   SetVideoFrameDelay(game_frame_delay_value);
11355
11356 #if 0
11357 #if 0
11358   /* ---------- main game synchronization point ---------- */
11359
11360   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11361
11362   printf("::: skip == %d\n", skip);
11363
11364 #else
11365   /* ---------- main game synchronization point ---------- */
11366
11367   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11368 #endif
11369 #endif
11370
11371   if (network_playing && !network_player_action_received)
11372   {
11373     /* try to get network player actions in time */
11374
11375 #if defined(NETWORK_AVALIABLE)
11376     /* last chance to get network player actions without main loop delay */
11377     HandleNetworking();
11378 #endif
11379
11380     /* game was quit by network peer */
11381     if (game_status != GAME_MODE_PLAYING)
11382       return;
11383
11384     if (!network_player_action_received)
11385       return;           /* failed to get network player actions in time */
11386
11387     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11388   }
11389
11390   if (tape.pausing)
11391     return;
11392
11393   /* at this point we know that we really continue executing the game */
11394
11395   network_player_action_received = FALSE;
11396
11397   /* when playing tape, read previously recorded player input from tape data */
11398   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11399
11400   local_player->effective_mouse_action = local_player->mouse_action;
11401
11402   if (recorded_player_action != NULL)
11403     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11404                                  recorded_player_action);
11405
11406   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11407   if (tape.pausing)
11408     return;
11409
11410   if (tape.set_centered_player)
11411   {
11412     game.centered_player_nr_next = tape.centered_player_nr_next;
11413     game.set_centered_player = TRUE;
11414   }
11415
11416   for (i = 0; i < MAX_PLAYERS; i++)
11417   {
11418     summarized_player_action |= stored_player[i].action;
11419
11420     if (!network_playing && (game.team_mode || tape.playing))
11421       stored_player[i].effective_action = stored_player[i].action;
11422   }
11423
11424 #if defined(NETWORK_AVALIABLE)
11425   if (network_playing)
11426     SendToServer_MovePlayer(summarized_player_action);
11427 #endif
11428
11429   // summarize all actions at local players mapped input device position
11430   // (this allows using different input devices in single player mode)
11431   if (!options.network && !game.team_mode)
11432     stored_player[map_player_action[local_player->index_nr]].effective_action =
11433       summarized_player_action;
11434
11435   if (tape.recording &&
11436       setup.team_mode &&
11437       setup.input_on_focus &&
11438       game.centered_player_nr != -1)
11439   {
11440     for (i = 0; i < MAX_PLAYERS; i++)
11441       stored_player[i].effective_action =
11442         (i == game.centered_player_nr ? summarized_player_action : 0);
11443   }
11444
11445   if (recorded_player_action != NULL)
11446     for (i = 0; i < MAX_PLAYERS; i++)
11447       stored_player[i].effective_action = recorded_player_action[i];
11448
11449   for (i = 0; i < MAX_PLAYERS; i++)
11450   {
11451     tape_action[i] = stored_player[i].effective_action;
11452
11453     /* (this may happen in the RND game engine if a player was not present on
11454        the playfield on level start, but appeared later from a custom element */
11455     if (setup.team_mode &&
11456         tape.recording &&
11457         tape_action[i] &&
11458         !tape.player_participates[i])
11459       tape.player_participates[i] = TRUE;
11460   }
11461
11462   SetTapeActionFromMouseAction(tape_action,
11463                                &local_player->effective_mouse_action);
11464
11465   /* only record actions from input devices, but not programmed actions */
11466   if (tape.recording)
11467     TapeRecordAction(tape_action);
11468
11469 #if USE_NEW_PLAYER_ASSIGNMENTS
11470   // !!! also map player actions in single player mode !!!
11471   // if (game.team_mode)
11472   if (1)
11473   {
11474     byte mapped_action[MAX_PLAYERS];
11475
11476 #if DEBUG_PLAYER_ACTIONS
11477     printf(":::");
11478     for (i = 0; i < MAX_PLAYERS; i++)
11479       printf(" %d, ", stored_player[i].effective_action);
11480 #endif
11481
11482     for (i = 0; i < MAX_PLAYERS; i++)
11483       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11484
11485     for (i = 0; i < MAX_PLAYERS; i++)
11486       stored_player[i].effective_action = mapped_action[i];
11487
11488 #if DEBUG_PLAYER_ACTIONS
11489     printf(" =>");
11490     for (i = 0; i < MAX_PLAYERS; i++)
11491       printf(" %d, ", stored_player[i].effective_action);
11492     printf("\n");
11493 #endif
11494   }
11495 #if DEBUG_PLAYER_ACTIONS
11496   else
11497   {
11498     printf(":::");
11499     for (i = 0; i < MAX_PLAYERS; i++)
11500       printf(" %d, ", stored_player[i].effective_action);
11501     printf("\n");
11502   }
11503 #endif
11504 #endif
11505
11506   for (i = 0; i < MAX_PLAYERS; i++)
11507   {
11508     // allow engine snapshot in case of changed movement attempt
11509     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11510         (stored_player[i].effective_action & KEY_MOTION))
11511       game.snapshot.changed_action = TRUE;
11512
11513     // allow engine snapshot in case of snapping/dropping attempt
11514     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11515         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11516       game.snapshot.changed_action = TRUE;
11517
11518     game.snapshot.last_action[i] = stored_player[i].effective_action;
11519   }
11520
11521   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11522   {
11523     GameActions_EM_Main();
11524   }
11525   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11526   {
11527     GameActions_SP_Main();
11528   }
11529   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11530   {
11531     GameActions_MM_Main();
11532   }
11533   else
11534   {
11535     GameActions_RND_Main();
11536   }
11537
11538   BlitScreenToBitmap(backbuffer);
11539
11540   CheckLevelTime();
11541
11542   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11543
11544   if (global.show_frames_per_second)
11545   {
11546     static unsigned int fps_counter = 0;
11547     static int fps_frames = 0;
11548     unsigned int fps_delay_ms = Counter() - fps_counter;
11549
11550     fps_frames++;
11551
11552     if (fps_delay_ms >= 500)    /* calculate FPS every 0.5 seconds */
11553     {
11554       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11555
11556       fps_frames = 0;
11557       fps_counter = Counter();
11558
11559       /* always draw FPS to screen after FPS value was updated */
11560       redraw_mask |= REDRAW_FPS;
11561     }
11562
11563     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11564     if (GetDrawDeactivationMask() == REDRAW_NONE)
11565       redraw_mask |= REDRAW_FPS;
11566   }
11567 }
11568
11569 static void GameActions_CheckSaveEngineSnapshot()
11570 {
11571   if (!game.snapshot.save_snapshot)
11572     return;
11573
11574   // clear flag for saving snapshot _before_ saving snapshot
11575   game.snapshot.save_snapshot = FALSE;
11576
11577   SaveEngineSnapshotToList();
11578 }
11579
11580 void GameActions()
11581 {
11582   GameActionsExt();
11583
11584   GameActions_CheckSaveEngineSnapshot();
11585 }
11586
11587 void GameActions_EM_Main()
11588 {
11589   byte effective_action[MAX_PLAYERS];
11590   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11591   int i;
11592
11593   for (i = 0; i < MAX_PLAYERS; i++)
11594     effective_action[i] = stored_player[i].effective_action;
11595
11596   GameActions_EM(effective_action, warp_mode);
11597 }
11598
11599 void GameActions_SP_Main()
11600 {
11601   byte effective_action[MAX_PLAYERS];
11602   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11603   int i;
11604
11605   for (i = 0; i < MAX_PLAYERS; i++)
11606     effective_action[i] = stored_player[i].effective_action;
11607
11608   GameActions_SP(effective_action, warp_mode);
11609
11610   for (i = 0; i < MAX_PLAYERS; i++)
11611   {
11612     if (stored_player[i].force_dropping)
11613       stored_player[i].action |= KEY_BUTTON_DROP;
11614
11615     stored_player[i].force_dropping = FALSE;
11616   }
11617 }
11618
11619 void GameActions_MM_Main()
11620 {
11621   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11622
11623   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11624 }
11625
11626 void GameActions_RND_Main()
11627 {
11628   GameActions_RND();
11629 }
11630
11631 void GameActions_RND()
11632 {
11633   int magic_wall_x = 0, magic_wall_y = 0;
11634   int i, x, y, element, graphic, last_gfx_frame;
11635
11636   InitPlayfieldScanModeVars();
11637
11638   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11639   {
11640     SCAN_PLAYFIELD(x, y)
11641     {
11642       ChangeCount[x][y] = 0;
11643       ChangeEvent[x][y] = -1;
11644     }
11645   }
11646
11647   if (game.set_centered_player)
11648   {
11649     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11650
11651     /* switching to "all players" only possible if all players fit to screen */
11652     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11653     {
11654       game.centered_player_nr_next = game.centered_player_nr;
11655       game.set_centered_player = FALSE;
11656     }
11657
11658     /* do not switch focus to non-existing (or non-active) player */
11659     if (game.centered_player_nr_next >= 0 &&
11660         !stored_player[game.centered_player_nr_next].active)
11661     {
11662       game.centered_player_nr_next = game.centered_player_nr;
11663       game.set_centered_player = FALSE;
11664     }
11665   }
11666
11667   if (game.set_centered_player &&
11668       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11669   {
11670     int sx, sy;
11671
11672     if (game.centered_player_nr_next == -1)
11673     {
11674       setScreenCenteredToAllPlayers(&sx, &sy);
11675     }
11676     else
11677     {
11678       sx = stored_player[game.centered_player_nr_next].jx;
11679       sy = stored_player[game.centered_player_nr_next].jy;
11680     }
11681
11682     game.centered_player_nr = game.centered_player_nr_next;
11683     game.set_centered_player = FALSE;
11684
11685     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11686     DrawGameDoorValues();
11687   }
11688
11689   for (i = 0; i < MAX_PLAYERS; i++)
11690   {
11691     int actual_player_action = stored_player[i].effective_action;
11692
11693 #if 1
11694     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11695        - rnd_equinox_tetrachloride 048
11696        - rnd_equinox_tetrachloride_ii 096
11697        - rnd_emanuel_schmieg 002
11698        - doctor_sloan_ww 001, 020
11699     */
11700     if (stored_player[i].MovPos == 0)
11701       CheckGravityMovement(&stored_player[i]);
11702 #endif
11703
11704     /* overwrite programmed action with tape action */
11705     if (stored_player[i].programmed_action)
11706       actual_player_action = stored_player[i].programmed_action;
11707
11708     PlayerActions(&stored_player[i], actual_player_action);
11709
11710     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11711   }
11712
11713   ScrollScreen(NULL, SCROLL_GO_ON);
11714
11715   /* for backwards compatibility, the following code emulates a fixed bug that
11716      occured when pushing elements (causing elements that just made their last
11717      pushing step to already (if possible) make their first falling step in the
11718      same game frame, which is bad); this code is also needed to use the famous
11719      "spring push bug" which is used in older levels and might be wanted to be
11720      used also in newer levels, but in this case the buggy pushing code is only
11721      affecting the "spring" element and no other elements */
11722
11723   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11724   {
11725     for (i = 0; i < MAX_PLAYERS; i++)
11726     {
11727       struct PlayerInfo *player = &stored_player[i];
11728       int x = player->jx;
11729       int y = player->jy;
11730
11731       if (player->active && player->is_pushing && player->is_moving &&
11732           IS_MOVING(x, y) &&
11733           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11734            Feld[x][y] == EL_SPRING))
11735       {
11736         ContinueMoving(x, y);
11737
11738         /* continue moving after pushing (this is actually a bug) */
11739         if (!IS_MOVING(x, y))
11740           Stop[x][y] = FALSE;
11741       }
11742     }
11743   }
11744
11745   SCAN_PLAYFIELD(x, y)
11746   {
11747     ChangeCount[x][y] = 0;
11748     ChangeEvent[x][y] = -1;
11749
11750     /* this must be handled before main playfield loop */
11751     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11752     {
11753       MovDelay[x][y]--;
11754       if (MovDelay[x][y] <= 0)
11755         RemoveField(x, y);
11756     }
11757
11758     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11759     {
11760       MovDelay[x][y]--;
11761       if (MovDelay[x][y] <= 0)
11762       {
11763         RemoveField(x, y);
11764         TEST_DrawLevelField(x, y);
11765
11766         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11767       }
11768     }
11769
11770 #if DEBUG
11771     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11772     {
11773       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11774       printf("GameActions(): This should never happen!\n");
11775
11776       ChangePage[x][y] = -1;
11777     }
11778 #endif
11779
11780     Stop[x][y] = FALSE;
11781     if (WasJustMoving[x][y] > 0)
11782       WasJustMoving[x][y]--;
11783     if (WasJustFalling[x][y] > 0)
11784       WasJustFalling[x][y]--;
11785     if (CheckCollision[x][y] > 0)
11786       CheckCollision[x][y]--;
11787     if (CheckImpact[x][y] > 0)
11788       CheckImpact[x][y]--;
11789
11790     GfxFrame[x][y]++;
11791
11792     /* reset finished pushing action (not done in ContinueMoving() to allow
11793        continuous pushing animation for elements with zero push delay) */
11794     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11795     {
11796       ResetGfxAnimation(x, y);
11797       TEST_DrawLevelField(x, y);
11798     }
11799
11800 #if DEBUG
11801     if (IS_BLOCKED(x, y))
11802     {
11803       int oldx, oldy;
11804
11805       Blocked2Moving(x, y, &oldx, &oldy);
11806       if (!IS_MOVING(oldx, oldy))
11807       {
11808         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11809         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11810         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11811         printf("GameActions(): This should never happen!\n");
11812       }
11813     }
11814 #endif
11815   }
11816
11817   SCAN_PLAYFIELD(x, y)
11818   {
11819     element = Feld[x][y];
11820     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11821     last_gfx_frame = GfxFrame[x][y];
11822
11823     ResetGfxFrame(x, y);
11824
11825     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11826       DrawLevelGraphicAnimation(x, y, graphic);
11827
11828     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11829         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11830       ResetRandomAnimationValue(x, y);
11831
11832     SetRandomAnimationValue(x, y);
11833
11834     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11835
11836     if (IS_INACTIVE(element))
11837     {
11838       if (IS_ANIMATED(graphic))
11839         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11840
11841       continue;
11842     }
11843
11844     /* this may take place after moving, so 'element' may have changed */
11845     if (IS_CHANGING(x, y) &&
11846         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11847     {
11848       int page = element_info[element].event_page_nr[CE_DELAY];
11849
11850       HandleElementChange(x, y, page);
11851
11852       element = Feld[x][y];
11853       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11854     }
11855
11856     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11857     {
11858       StartMoving(x, y);
11859
11860       element = Feld[x][y];
11861       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11862
11863       if (IS_ANIMATED(graphic) &&
11864           !IS_MOVING(x, y) &&
11865           !Stop[x][y])
11866         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11867
11868       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11869         TEST_DrawTwinkleOnField(x, y);
11870     }
11871     else if (element == EL_ACID)
11872     {
11873       if (!Stop[x][y])
11874         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11875     }
11876     else if ((element == EL_EXIT_OPEN ||
11877               element == EL_EM_EXIT_OPEN ||
11878               element == EL_SP_EXIT_OPEN ||
11879               element == EL_STEEL_EXIT_OPEN ||
11880               element == EL_EM_STEEL_EXIT_OPEN ||
11881               element == EL_SP_TERMINAL ||
11882               element == EL_SP_TERMINAL_ACTIVE ||
11883               element == EL_EXTRA_TIME ||
11884               element == EL_SHIELD_NORMAL ||
11885               element == EL_SHIELD_DEADLY) &&
11886              IS_ANIMATED(graphic))
11887       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11888     else if (IS_MOVING(x, y))
11889       ContinueMoving(x, y);
11890     else if (IS_ACTIVE_BOMB(element))
11891       CheckDynamite(x, y);
11892     else if (element == EL_AMOEBA_GROWING)
11893       AmoebeWaechst(x, y);
11894     else if (element == EL_AMOEBA_SHRINKING)
11895       AmoebaDisappearing(x, y);
11896
11897 #if !USE_NEW_AMOEBA_CODE
11898     else if (IS_AMOEBALIVE(element))
11899       AmoebeAbleger(x, y);
11900 #endif
11901
11902     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11903       Life(x, y);
11904     else if (element == EL_EXIT_CLOSED)
11905       CheckExit(x, y);
11906     else if (element == EL_EM_EXIT_CLOSED)
11907       CheckExitEM(x, y);
11908     else if (element == EL_STEEL_EXIT_CLOSED)
11909       CheckExitSteel(x, y);
11910     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11911       CheckExitSteelEM(x, y);
11912     else if (element == EL_SP_EXIT_CLOSED)
11913       CheckExitSP(x, y);
11914     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11915              element == EL_EXPANDABLE_STEELWALL_GROWING)
11916       MauerWaechst(x, y);
11917     else if (element == EL_EXPANDABLE_WALL ||
11918              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11919              element == EL_EXPANDABLE_WALL_VERTICAL ||
11920              element == EL_EXPANDABLE_WALL_ANY ||
11921              element == EL_BD_EXPANDABLE_WALL)
11922       MauerAbleger(x, y);
11923     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11924              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11925              element == EL_EXPANDABLE_STEELWALL_ANY)
11926       MauerAblegerStahl(x, y);
11927     else if (element == EL_FLAMES)
11928       CheckForDragon(x, y);
11929     else if (element == EL_EXPLOSION)
11930       ; /* drawing of correct explosion animation is handled separately */
11931     else if (element == EL_ELEMENT_SNAPPING ||
11932              element == EL_DIAGONAL_SHRINKING ||
11933              element == EL_DIAGONAL_GROWING)
11934     {
11935       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11936
11937       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11938     }
11939     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11940       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11941
11942     if (IS_BELT_ACTIVE(element))
11943       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11944
11945     if (game.magic_wall_active)
11946     {
11947       int jx = local_player->jx, jy = local_player->jy;
11948
11949       /* play the element sound at the position nearest to the player */
11950       if ((element == EL_MAGIC_WALL_FULL ||
11951            element == EL_MAGIC_WALL_ACTIVE ||
11952            element == EL_MAGIC_WALL_EMPTYING ||
11953            element == EL_BD_MAGIC_WALL_FULL ||
11954            element == EL_BD_MAGIC_WALL_ACTIVE ||
11955            element == EL_BD_MAGIC_WALL_EMPTYING ||
11956            element == EL_DC_MAGIC_WALL_FULL ||
11957            element == EL_DC_MAGIC_WALL_ACTIVE ||
11958            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11959           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11960       {
11961         magic_wall_x = x;
11962         magic_wall_y = y;
11963       }
11964     }
11965   }
11966
11967 #if USE_NEW_AMOEBA_CODE
11968   /* new experimental amoeba growth stuff */
11969   if (!(FrameCounter % 8))
11970   {
11971     static unsigned int random = 1684108901;
11972
11973     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11974     {
11975       x = RND(lev_fieldx);
11976       y = RND(lev_fieldy);
11977       element = Feld[x][y];
11978
11979       if (!IS_PLAYER(x,y) &&
11980           (element == EL_EMPTY ||
11981            CAN_GROW_INTO(element) ||
11982            element == EL_QUICKSAND_EMPTY ||
11983            element == EL_QUICKSAND_FAST_EMPTY ||
11984            element == EL_ACID_SPLASH_LEFT ||
11985            element == EL_ACID_SPLASH_RIGHT))
11986       {
11987         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11988             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11989             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11990             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11991           Feld[x][y] = EL_AMOEBA_DROP;
11992       }
11993
11994       random = random * 129 + 1;
11995     }
11996   }
11997 #endif
11998
11999   game.explosions_delayed = FALSE;
12000
12001   SCAN_PLAYFIELD(x, y)
12002   {
12003     element = Feld[x][y];
12004
12005     if (ExplodeField[x][y])
12006       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12007     else if (element == EL_EXPLOSION)
12008       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12009
12010     ExplodeField[x][y] = EX_TYPE_NONE;
12011   }
12012
12013   game.explosions_delayed = TRUE;
12014
12015   if (game.magic_wall_active)
12016   {
12017     if (!(game.magic_wall_time_left % 4))
12018     {
12019       int element = Feld[magic_wall_x][magic_wall_y];
12020
12021       if (element == EL_BD_MAGIC_WALL_FULL ||
12022           element == EL_BD_MAGIC_WALL_ACTIVE ||
12023           element == EL_BD_MAGIC_WALL_EMPTYING)
12024         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12025       else if (element == EL_DC_MAGIC_WALL_FULL ||
12026                element == EL_DC_MAGIC_WALL_ACTIVE ||
12027                element == EL_DC_MAGIC_WALL_EMPTYING)
12028         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12029       else
12030         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12031     }
12032
12033     if (game.magic_wall_time_left > 0)
12034     {
12035       game.magic_wall_time_left--;
12036
12037       if (!game.magic_wall_time_left)
12038       {
12039         SCAN_PLAYFIELD(x, y)
12040         {
12041           element = Feld[x][y];
12042
12043           if (element == EL_MAGIC_WALL_ACTIVE ||
12044               element == EL_MAGIC_WALL_FULL)
12045           {
12046             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12047             TEST_DrawLevelField(x, y);
12048           }
12049           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12050                    element == EL_BD_MAGIC_WALL_FULL)
12051           {
12052             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12053             TEST_DrawLevelField(x, y);
12054           }
12055           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12056                    element == EL_DC_MAGIC_WALL_FULL)
12057           {
12058             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12059             TEST_DrawLevelField(x, y);
12060           }
12061         }
12062
12063         game.magic_wall_active = FALSE;
12064       }
12065     }
12066   }
12067
12068   if (game.light_time_left > 0)
12069   {
12070     game.light_time_left--;
12071
12072     if (game.light_time_left == 0)
12073       RedrawAllLightSwitchesAndInvisibleElements();
12074   }
12075
12076   if (game.timegate_time_left > 0)
12077   {
12078     game.timegate_time_left--;
12079
12080     if (game.timegate_time_left == 0)
12081       CloseAllOpenTimegates();
12082   }
12083
12084   if (game.lenses_time_left > 0)
12085   {
12086     game.lenses_time_left--;
12087
12088     if (game.lenses_time_left == 0)
12089       RedrawAllInvisibleElementsForLenses();
12090   }
12091
12092   if (game.magnify_time_left > 0)
12093   {
12094     game.magnify_time_left--;
12095
12096     if (game.magnify_time_left == 0)
12097       RedrawAllInvisibleElementsForMagnifier();
12098   }
12099
12100   for (i = 0; i < MAX_PLAYERS; i++)
12101   {
12102     struct PlayerInfo *player = &stored_player[i];
12103
12104     if (SHIELD_ON(player))
12105     {
12106       if (player->shield_deadly_time_left)
12107         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12108       else if (player->shield_normal_time_left)
12109         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12110     }
12111   }
12112
12113 #if USE_DELAYED_GFX_REDRAW
12114   SCAN_PLAYFIELD(x, y)
12115   {
12116     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12117     {
12118       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12119          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12120
12121       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12122         DrawLevelField(x, y);
12123
12124       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12125         DrawLevelFieldCrumbled(x, y);
12126
12127       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12128         DrawLevelFieldCrumbledNeighbours(x, y);
12129
12130       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12131         DrawTwinkleOnField(x, y);
12132     }
12133
12134     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12135   }
12136 #endif
12137
12138   DrawAllPlayers();
12139   PlayAllPlayersSound();
12140
12141   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12142   {
12143     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12144
12145     local_player->show_envelope = 0;
12146   }
12147
12148   /* use random number generator in every frame to make it less predictable */
12149   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12150     RND(1);
12151 }
12152
12153 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12154 {
12155   int min_x = x, min_y = y, max_x = x, max_y = y;
12156   int i;
12157
12158   for (i = 0; i < MAX_PLAYERS; i++)
12159   {
12160     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12161
12162     if (!stored_player[i].active || &stored_player[i] == player)
12163       continue;
12164
12165     min_x = MIN(min_x, jx);
12166     min_y = MIN(min_y, jy);
12167     max_x = MAX(max_x, jx);
12168     max_y = MAX(max_y, jy);
12169   }
12170
12171   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12172 }
12173
12174 static boolean AllPlayersInVisibleScreen()
12175 {
12176   int i;
12177
12178   for (i = 0; i < MAX_PLAYERS; i++)
12179   {
12180     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12181
12182     if (!stored_player[i].active)
12183       continue;
12184
12185     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12186       return FALSE;
12187   }
12188
12189   return TRUE;
12190 }
12191
12192 void ScrollLevel(int dx, int dy)
12193 {
12194   int scroll_offset = 2 * TILEX_VAR;
12195   int x, y;
12196
12197   BlitBitmap(drawto_field, drawto_field,
12198              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12199              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12200              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12201              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12202              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12203              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12204
12205   if (dx != 0)
12206   {
12207     x = (dx == 1 ? BX1 : BX2);
12208     for (y = BY1; y <= BY2; y++)
12209       DrawScreenField(x, y);
12210   }
12211
12212   if (dy != 0)
12213   {
12214     y = (dy == 1 ? BY1 : BY2);
12215     for (x = BX1; x <= BX2; x++)
12216       DrawScreenField(x, y);
12217   }
12218
12219   redraw_mask |= REDRAW_FIELD;
12220 }
12221
12222 static boolean canFallDown(struct PlayerInfo *player)
12223 {
12224   int jx = player->jx, jy = player->jy;
12225
12226   return (IN_LEV_FIELD(jx, jy + 1) &&
12227           (IS_FREE(jx, jy + 1) ||
12228            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12229           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12230           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12231 }
12232
12233 static boolean canPassField(int x, int y, int move_dir)
12234 {
12235   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12236   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12237   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12238   int nextx = x + dx;
12239   int nexty = y + dy;
12240   int element = Feld[x][y];
12241
12242   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12243           !CAN_MOVE(element) &&
12244           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12245           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12246           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12247 }
12248
12249 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12250 {
12251   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12252   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12253   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12254   int newx = x + dx;
12255   int newy = y + dy;
12256
12257   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12258           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12259           (IS_DIGGABLE(Feld[newx][newy]) ||
12260            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12261            canPassField(newx, newy, move_dir)));
12262 }
12263
12264 static void CheckGravityMovement(struct PlayerInfo *player)
12265 {
12266   if (player->gravity && !player->programmed_action)
12267   {
12268     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12269     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12270     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12271     int jx = player->jx, jy = player->jy;
12272     boolean player_is_moving_to_valid_field =
12273       (!player_is_snapping &&
12274        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12275         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12276     boolean player_can_fall_down = canFallDown(player);
12277
12278     if (player_can_fall_down &&
12279         !player_is_moving_to_valid_field)
12280       player->programmed_action = MV_DOWN;
12281   }
12282 }
12283
12284 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12285 {
12286   return CheckGravityMovement(player);
12287
12288   if (player->gravity && !player->programmed_action)
12289   {
12290     int jx = player->jx, jy = player->jy;
12291     boolean field_under_player_is_free =
12292       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12293     boolean player_is_standing_on_valid_field =
12294       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12295        (IS_WALKABLE(Feld[jx][jy]) &&
12296         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12297
12298     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12299       player->programmed_action = MV_DOWN;
12300   }
12301 }
12302
12303 /*
12304   MovePlayerOneStep()
12305   -----------------------------------------------------------------------------
12306   dx, dy:               direction (non-diagonal) to try to move the player to
12307   real_dx, real_dy:     direction as read from input device (can be diagonal)
12308 */
12309
12310 boolean MovePlayerOneStep(struct PlayerInfo *player,
12311                           int dx, int dy, int real_dx, int real_dy)
12312 {
12313   int jx = player->jx, jy = player->jy;
12314   int new_jx = jx + dx, new_jy = jy + dy;
12315   int can_move;
12316   boolean player_can_move = !player->cannot_move;
12317
12318   if (!player->active || (!dx && !dy))
12319     return MP_NO_ACTION;
12320
12321   player->MovDir = (dx < 0 ? MV_LEFT :
12322                     dx > 0 ? MV_RIGHT :
12323                     dy < 0 ? MV_UP :
12324                     dy > 0 ? MV_DOWN :  MV_NONE);
12325
12326   if (!IN_LEV_FIELD(new_jx, new_jy))
12327     return MP_NO_ACTION;
12328
12329   if (!player_can_move)
12330   {
12331     if (player->MovPos == 0)
12332     {
12333       player->is_moving = FALSE;
12334       player->is_digging = FALSE;
12335       player->is_collecting = FALSE;
12336       player->is_snapping = FALSE;
12337       player->is_pushing = FALSE;
12338     }
12339   }
12340
12341   if (!options.network && game.centered_player_nr == -1 &&
12342       !AllPlayersInSight(player, new_jx, new_jy))
12343     return MP_NO_ACTION;
12344
12345   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12346   if (can_move != MP_MOVING)
12347     return can_move;
12348
12349   /* check if DigField() has caused relocation of the player */
12350   if (player->jx != jx || player->jy != jy)
12351     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12352
12353   StorePlayer[jx][jy] = 0;
12354   player->last_jx = jx;
12355   player->last_jy = jy;
12356   player->jx = new_jx;
12357   player->jy = new_jy;
12358   StorePlayer[new_jx][new_jy] = player->element_nr;
12359
12360   if (player->move_delay_value_next != -1)
12361   {
12362     player->move_delay_value = player->move_delay_value_next;
12363     player->move_delay_value_next = -1;
12364   }
12365
12366   player->MovPos =
12367     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12368
12369   player->step_counter++;
12370
12371   PlayerVisit[jx][jy] = FrameCounter;
12372
12373   player->is_moving = TRUE;
12374
12375 #if 1
12376   /* should better be called in MovePlayer(), but this breaks some tapes */
12377   ScrollPlayer(player, SCROLL_INIT);
12378 #endif
12379
12380   return MP_MOVING;
12381 }
12382
12383 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12384 {
12385   int jx = player->jx, jy = player->jy;
12386   int old_jx = jx, old_jy = jy;
12387   int moved = MP_NO_ACTION;
12388
12389   if (!player->active)
12390     return FALSE;
12391
12392   if (!dx && !dy)
12393   {
12394     if (player->MovPos == 0)
12395     {
12396       player->is_moving = FALSE;
12397       player->is_digging = FALSE;
12398       player->is_collecting = FALSE;
12399       player->is_snapping = FALSE;
12400       player->is_pushing = FALSE;
12401     }
12402
12403     return FALSE;
12404   }
12405
12406   if (player->move_delay > 0)
12407     return FALSE;
12408
12409   player->move_delay = -1;              /* set to "uninitialized" value */
12410
12411   /* store if player is automatically moved to next field */
12412   player->is_auto_moving = (player->programmed_action != MV_NONE);
12413
12414   /* remove the last programmed player action */
12415   player->programmed_action = 0;
12416
12417   if (player->MovPos)
12418   {
12419     /* should only happen if pre-1.2 tape recordings are played */
12420     /* this is only for backward compatibility */
12421
12422     int original_move_delay_value = player->move_delay_value;
12423
12424 #if DEBUG
12425     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12426            tape.counter);
12427 #endif
12428
12429     /* scroll remaining steps with finest movement resolution */
12430     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12431
12432     while (player->MovPos)
12433     {
12434       ScrollPlayer(player, SCROLL_GO_ON);
12435       ScrollScreen(NULL, SCROLL_GO_ON);
12436
12437       AdvanceFrameAndPlayerCounters(player->index_nr);
12438
12439       DrawAllPlayers();
12440       BackToFront_WithFrameDelay(0);
12441     }
12442
12443     player->move_delay_value = original_move_delay_value;
12444   }
12445
12446   player->is_active = FALSE;
12447
12448   if (player->last_move_dir & MV_HORIZONTAL)
12449   {
12450     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12451       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12452   }
12453   else
12454   {
12455     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12456       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12457   }
12458
12459   if (!moved && !player->is_active)
12460   {
12461     player->is_moving = FALSE;
12462     player->is_digging = FALSE;
12463     player->is_collecting = FALSE;
12464     player->is_snapping = FALSE;
12465     player->is_pushing = FALSE;
12466   }
12467
12468   jx = player->jx;
12469   jy = player->jy;
12470
12471   if (moved & MP_MOVING && !ScreenMovPos &&
12472       (player->index_nr == game.centered_player_nr ||
12473        game.centered_player_nr == -1))
12474   {
12475     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12476     int offset = game.scroll_delay_value;
12477
12478     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12479     {
12480       /* actual player has left the screen -- scroll in that direction */
12481       if (jx != old_jx)         /* player has moved horizontally */
12482         scroll_x += (jx - old_jx);
12483       else                      /* player has moved vertically */
12484         scroll_y += (jy - old_jy);
12485     }
12486     else
12487     {
12488       if (jx != old_jx)         /* player has moved horizontally */
12489       {
12490         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12491             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12492           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12493
12494         /* don't scroll over playfield boundaries */
12495         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12496           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12497
12498         /* don't scroll more than one field at a time */
12499         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12500
12501         /* don't scroll against the player's moving direction */
12502         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12503             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12504           scroll_x = old_scroll_x;
12505       }
12506       else                      /* player has moved vertically */
12507       {
12508         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12509             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12510           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12511
12512         /* don't scroll over playfield boundaries */
12513         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12514           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12515
12516         /* don't scroll more than one field at a time */
12517         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12518
12519         /* don't scroll against the player's moving direction */
12520         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12521             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12522           scroll_y = old_scroll_y;
12523       }
12524     }
12525
12526     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12527     {
12528       if (!options.network && game.centered_player_nr == -1 &&
12529           !AllPlayersInVisibleScreen())
12530       {
12531         scroll_x = old_scroll_x;
12532         scroll_y = old_scroll_y;
12533       }
12534       else
12535       {
12536         ScrollScreen(player, SCROLL_INIT);
12537         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12538       }
12539     }
12540   }
12541
12542   player->StepFrame = 0;
12543
12544   if (moved & MP_MOVING)
12545   {
12546     if (old_jx != jx && old_jy == jy)
12547       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12548     else if (old_jx == jx && old_jy != jy)
12549       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12550
12551     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12552
12553     player->last_move_dir = player->MovDir;
12554     player->is_moving = TRUE;
12555     player->is_snapping = FALSE;
12556     player->is_switching = FALSE;
12557     player->is_dropping = FALSE;
12558     player->is_dropping_pressed = FALSE;
12559     player->drop_pressed_delay = 0;
12560
12561 #if 0
12562     /* should better be called here than above, but this breaks some tapes */
12563     ScrollPlayer(player, SCROLL_INIT);
12564 #endif
12565   }
12566   else
12567   {
12568     CheckGravityMovementWhenNotMoving(player);
12569
12570     player->is_moving = FALSE;
12571
12572     /* at this point, the player is allowed to move, but cannot move right now
12573        (e.g. because of something blocking the way) -- ensure that the player
12574        is also allowed to move in the next frame (in old versions before 3.1.1,
12575        the player was forced to wait again for eight frames before next try) */
12576
12577     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12578       player->move_delay = 0;   /* allow direct movement in the next frame */
12579   }
12580
12581   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12582     player->move_delay = player->move_delay_value;
12583
12584   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12585   {
12586     TestIfPlayerTouchesBadThing(jx, jy);
12587     TestIfPlayerTouchesCustomElement(jx, jy);
12588   }
12589
12590   if (!player->active)
12591     RemovePlayer(player);
12592
12593   return moved;
12594 }
12595
12596 void ScrollPlayer(struct PlayerInfo *player, int mode)
12597 {
12598   int jx = player->jx, jy = player->jy;
12599   int last_jx = player->last_jx, last_jy = player->last_jy;
12600   int move_stepsize = TILEX / player->move_delay_value;
12601
12602   if (!player->active)
12603     return;
12604
12605   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12606     return;
12607
12608   if (mode == SCROLL_INIT)
12609   {
12610     player->actual_frame_counter = FrameCounter;
12611     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12612
12613     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12614         Feld[last_jx][last_jy] == EL_EMPTY)
12615     {
12616       int last_field_block_delay = 0;   /* start with no blocking at all */
12617       int block_delay_adjustment = player->block_delay_adjustment;
12618
12619       /* if player blocks last field, add delay for exactly one move */
12620       if (player->block_last_field)
12621       {
12622         last_field_block_delay += player->move_delay_value;
12623
12624         /* when blocking enabled, prevent moving up despite gravity */
12625         if (player->gravity && player->MovDir == MV_UP)
12626           block_delay_adjustment = -1;
12627       }
12628
12629       /* add block delay adjustment (also possible when not blocking) */
12630       last_field_block_delay += block_delay_adjustment;
12631
12632       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12633       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12634     }
12635
12636     if (player->MovPos != 0)    /* player has not yet reached destination */
12637       return;
12638   }
12639   else if (!FrameReached(&player->actual_frame_counter, 1))
12640     return;
12641
12642   if (player->MovPos != 0)
12643   {
12644     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12645     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12646
12647     /* before DrawPlayer() to draw correct player graphic for this case */
12648     if (player->MovPos == 0)
12649       CheckGravityMovement(player);
12650   }
12651
12652   if (player->MovPos == 0)      /* player reached destination field */
12653   {
12654     if (player->move_delay_reset_counter > 0)
12655     {
12656       player->move_delay_reset_counter--;
12657
12658       if (player->move_delay_reset_counter == 0)
12659       {
12660         /* continue with normal speed after quickly moving through gate */
12661         HALVE_PLAYER_SPEED(player);
12662
12663         /* be able to make the next move without delay */
12664         player->move_delay = 0;
12665       }
12666     }
12667
12668     player->last_jx = jx;
12669     player->last_jy = jy;
12670
12671     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12672         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12673         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12674         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12675         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12676         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12677         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12678         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12679     {
12680       DrawPlayer(player);       /* needed here only to cleanup last field */
12681       RemovePlayer(player);
12682
12683       if (local_player->friends_still_needed == 0 ||
12684           IS_SP_ELEMENT(Feld[jx][jy]))
12685         PlayerWins(player);
12686     }
12687
12688     /* this breaks one level: "machine", level 000 */
12689     {
12690       int move_direction = player->MovDir;
12691       int enter_side = MV_DIR_OPPOSITE(move_direction);
12692       int leave_side = move_direction;
12693       int old_jx = last_jx;
12694       int old_jy = last_jy;
12695       int old_element = Feld[old_jx][old_jy];
12696       int new_element = Feld[jx][jy];
12697
12698       if (IS_CUSTOM_ELEMENT(old_element))
12699         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12700                                    CE_LEFT_BY_PLAYER,
12701                                    player->index_bit, leave_side);
12702
12703       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12704                                           CE_PLAYER_LEAVES_X,
12705                                           player->index_bit, leave_side);
12706
12707       if (IS_CUSTOM_ELEMENT(new_element))
12708         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12709                                    player->index_bit, enter_side);
12710
12711       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12712                                           CE_PLAYER_ENTERS_X,
12713                                           player->index_bit, enter_side);
12714
12715       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12716                                         CE_MOVE_OF_X, move_direction);
12717     }
12718
12719     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12720     {
12721       TestIfPlayerTouchesBadThing(jx, jy);
12722       TestIfPlayerTouchesCustomElement(jx, jy);
12723
12724       /* needed because pushed element has not yet reached its destination,
12725          so it would trigger a change event at its previous field location */
12726       if (!player->is_pushing)
12727         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12728
12729       if (!player->active)
12730         RemovePlayer(player);
12731     }
12732
12733     if (!local_player->LevelSolved && level.use_step_counter)
12734     {
12735       int i;
12736
12737       TimePlayed++;
12738
12739       if (TimeLeft > 0)
12740       {
12741         TimeLeft--;
12742
12743         if (TimeLeft <= 10 && setup.time_limit)
12744           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12745
12746         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12747
12748         DisplayGameControlValues();
12749
12750         if (!TimeLeft && setup.time_limit)
12751           for (i = 0; i < MAX_PLAYERS; i++)
12752             KillPlayer(&stored_player[i]);
12753       }
12754       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12755       {
12756         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12757
12758         DisplayGameControlValues();
12759       }
12760     }
12761
12762     if (tape.single_step && tape.recording && !tape.pausing &&
12763         !player->programmed_action)
12764       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12765
12766     if (!player->programmed_action)
12767       CheckSaveEngineSnapshot(player);
12768   }
12769 }
12770
12771 void ScrollScreen(struct PlayerInfo *player, int mode)
12772 {
12773   static unsigned int screen_frame_counter = 0;
12774
12775   if (mode == SCROLL_INIT)
12776   {
12777     /* set scrolling step size according to actual player's moving speed */
12778     ScrollStepSize = TILEX / player->move_delay_value;
12779
12780     screen_frame_counter = FrameCounter;
12781     ScreenMovDir = player->MovDir;
12782     ScreenMovPos = player->MovPos;
12783     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12784     return;
12785   }
12786   else if (!FrameReached(&screen_frame_counter, 1))
12787     return;
12788
12789   if (ScreenMovPos)
12790   {
12791     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12792     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12793     redraw_mask |= REDRAW_FIELD;
12794   }
12795   else
12796     ScreenMovDir = MV_NONE;
12797 }
12798
12799 void TestIfPlayerTouchesCustomElement(int x, int y)
12800 {
12801   static int xy[4][2] =
12802   {
12803     { 0, -1 },
12804     { -1, 0 },
12805     { +1, 0 },
12806     { 0, +1 }
12807   };
12808   static int trigger_sides[4][2] =
12809   {
12810     /* center side       border side */
12811     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12812     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12813     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12814     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12815   };
12816   static int touch_dir[4] =
12817   {
12818     MV_LEFT | MV_RIGHT,
12819     MV_UP   | MV_DOWN,
12820     MV_UP   | MV_DOWN,
12821     MV_LEFT | MV_RIGHT
12822   };
12823   int center_element = Feld[x][y];      /* should always be non-moving! */
12824   int i;
12825
12826   for (i = 0; i < NUM_DIRECTIONS; i++)
12827   {
12828     int xx = x + xy[i][0];
12829     int yy = y + xy[i][1];
12830     int center_side = trigger_sides[i][0];
12831     int border_side = trigger_sides[i][1];
12832     int border_element;
12833
12834     if (!IN_LEV_FIELD(xx, yy))
12835       continue;
12836
12837     if (IS_PLAYER(x, y))                /* player found at center element */
12838     {
12839       struct PlayerInfo *player = PLAYERINFO(x, y);
12840
12841       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12842         border_element = Feld[xx][yy];          /* may be moving! */
12843       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12844         border_element = Feld[xx][yy];
12845       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12846         border_element = MovingOrBlocked2Element(xx, yy);
12847       else
12848         continue;               /* center and border element do not touch */
12849
12850       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12851                                  player->index_bit, border_side);
12852       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12853                                           CE_PLAYER_TOUCHES_X,
12854                                           player->index_bit, border_side);
12855
12856       {
12857         /* use player element that is initially defined in the level playfield,
12858            not the player element that corresponds to the runtime player number
12859            (example: a level that contains EL_PLAYER_3 as the only player would
12860            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12861         int player_element = PLAYERINFO(x, y)->initial_element;
12862
12863         CheckElementChangeBySide(xx, yy, border_element, player_element,
12864                                  CE_TOUCHING_X, border_side);
12865       }
12866     }
12867     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12868     {
12869       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12870
12871       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12872       {
12873         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12874           continue;             /* center and border element do not touch */
12875       }
12876
12877       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12878                                  player->index_bit, center_side);
12879       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12880                                           CE_PLAYER_TOUCHES_X,
12881                                           player->index_bit, center_side);
12882
12883       {
12884         /* use player element that is initially defined in the level playfield,
12885            not the player element that corresponds to the runtime player number
12886            (example: a level that contains EL_PLAYER_3 as the only player would
12887            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12888         int player_element = PLAYERINFO(xx, yy)->initial_element;
12889
12890         CheckElementChangeBySide(x, y, center_element, player_element,
12891                                  CE_TOUCHING_X, center_side);
12892       }
12893
12894       break;
12895     }
12896   }
12897 }
12898
12899 void TestIfElementTouchesCustomElement(int x, int y)
12900 {
12901   static int xy[4][2] =
12902   {
12903     { 0, -1 },
12904     { -1, 0 },
12905     { +1, 0 },
12906     { 0, +1 }
12907   };
12908   static int trigger_sides[4][2] =
12909   {
12910     /* center side      border side */
12911     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12912     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12913     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12914     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12915   };
12916   static int touch_dir[4] =
12917   {
12918     MV_LEFT | MV_RIGHT,
12919     MV_UP   | MV_DOWN,
12920     MV_UP   | MV_DOWN,
12921     MV_LEFT | MV_RIGHT
12922   };
12923   boolean change_center_element = FALSE;
12924   int center_element = Feld[x][y];      /* should always be non-moving! */
12925   int border_element_old[NUM_DIRECTIONS];
12926   int i;
12927
12928   for (i = 0; i < NUM_DIRECTIONS; i++)
12929   {
12930     int xx = x + xy[i][0];
12931     int yy = y + xy[i][1];
12932     int border_element;
12933
12934     border_element_old[i] = -1;
12935
12936     if (!IN_LEV_FIELD(xx, yy))
12937       continue;
12938
12939     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12940       border_element = Feld[xx][yy];    /* may be moving! */
12941     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12942       border_element = Feld[xx][yy];
12943     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12944       border_element = MovingOrBlocked2Element(xx, yy);
12945     else
12946       continue;                 /* center and border element do not touch */
12947
12948     border_element_old[i] = border_element;
12949   }
12950
12951   for (i = 0; i < NUM_DIRECTIONS; i++)
12952   {
12953     int xx = x + xy[i][0];
12954     int yy = y + xy[i][1];
12955     int center_side = trigger_sides[i][0];
12956     int border_element = border_element_old[i];
12957
12958     if (border_element == -1)
12959       continue;
12960
12961     /* check for change of border element */
12962     CheckElementChangeBySide(xx, yy, border_element, center_element,
12963                              CE_TOUCHING_X, center_side);
12964
12965     /* (center element cannot be player, so we dont have to check this here) */
12966   }
12967
12968   for (i = 0; i < NUM_DIRECTIONS; i++)
12969   {
12970     int xx = x + xy[i][0];
12971     int yy = y + xy[i][1];
12972     int border_side = trigger_sides[i][1];
12973     int border_element = border_element_old[i];
12974
12975     if (border_element == -1)
12976       continue;
12977
12978     /* check for change of center element (but change it only once) */
12979     if (!change_center_element)
12980       change_center_element =
12981         CheckElementChangeBySide(x, y, center_element, border_element,
12982                                  CE_TOUCHING_X, border_side);
12983
12984     if (IS_PLAYER(xx, yy))
12985     {
12986       /* use player element that is initially defined in the level playfield,
12987          not the player element that corresponds to the runtime player number
12988          (example: a level that contains EL_PLAYER_3 as the only player would
12989          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12990       int player_element = PLAYERINFO(xx, yy)->initial_element;
12991
12992       CheckElementChangeBySide(x, y, center_element, player_element,
12993                                CE_TOUCHING_X, border_side);
12994     }
12995   }
12996 }
12997
12998 void TestIfElementHitsCustomElement(int x, int y, int direction)
12999 {
13000   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13001   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13002   int hitx = x + dx, hity = y + dy;
13003   int hitting_element = Feld[x][y];
13004   int touched_element;
13005
13006   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13007     return;
13008
13009   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13010                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13011
13012   if (IN_LEV_FIELD(hitx, hity))
13013   {
13014     int opposite_direction = MV_DIR_OPPOSITE(direction);
13015     int hitting_side = direction;
13016     int touched_side = opposite_direction;
13017     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13018                           MovDir[hitx][hity] != direction ||
13019                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13020
13021     object_hit = TRUE;
13022
13023     if (object_hit)
13024     {
13025       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13026                                CE_HITTING_X, touched_side);
13027
13028       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13029                                CE_HIT_BY_X, hitting_side);
13030
13031       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13032                                CE_HIT_BY_SOMETHING, opposite_direction);
13033
13034       if (IS_PLAYER(hitx, hity))
13035       {
13036         /* use player element that is initially defined in the level playfield,
13037            not the player element that corresponds to the runtime player number
13038            (example: a level that contains EL_PLAYER_3 as the only player would
13039            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13040         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13041
13042         CheckElementChangeBySide(x, y, hitting_element, player_element,
13043                                  CE_HITTING_X, touched_side);
13044       }
13045     }
13046   }
13047
13048   /* "hitting something" is also true when hitting the playfield border */
13049   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13050                            CE_HITTING_SOMETHING, direction);
13051 }
13052
13053 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13054 {
13055   int i, kill_x = -1, kill_y = -1;
13056
13057   int bad_element = -1;
13058   static int test_xy[4][2] =
13059   {
13060     { 0, -1 },
13061     { -1, 0 },
13062     { +1, 0 },
13063     { 0, +1 }
13064   };
13065   static int test_dir[4] =
13066   {
13067     MV_UP,
13068     MV_LEFT,
13069     MV_RIGHT,
13070     MV_DOWN
13071   };
13072
13073   for (i = 0; i < NUM_DIRECTIONS; i++)
13074   {
13075     int test_x, test_y, test_move_dir, test_element;
13076
13077     test_x = good_x + test_xy[i][0];
13078     test_y = good_y + test_xy[i][1];
13079
13080     if (!IN_LEV_FIELD(test_x, test_y))
13081       continue;
13082
13083     test_move_dir =
13084       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13085
13086     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13087
13088     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13089        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13090     */
13091     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13092         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13093     {
13094       kill_x = test_x;
13095       kill_y = test_y;
13096       bad_element = test_element;
13097
13098       break;
13099     }
13100   }
13101
13102   if (kill_x != -1 || kill_y != -1)
13103   {
13104     if (IS_PLAYER(good_x, good_y))
13105     {
13106       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13107
13108       if (player->shield_deadly_time_left > 0 &&
13109           !IS_INDESTRUCTIBLE(bad_element))
13110         Bang(kill_x, kill_y);
13111       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13112         KillPlayer(player);
13113     }
13114     else
13115       Bang(good_x, good_y);
13116   }
13117 }
13118
13119 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13120 {
13121   int i, kill_x = -1, kill_y = -1;
13122   int bad_element = Feld[bad_x][bad_y];
13123   static int test_xy[4][2] =
13124   {
13125     { 0, -1 },
13126     { -1, 0 },
13127     { +1, 0 },
13128     { 0, +1 }
13129   };
13130   static int touch_dir[4] =
13131   {
13132     MV_LEFT | MV_RIGHT,
13133     MV_UP   | MV_DOWN,
13134     MV_UP   | MV_DOWN,
13135     MV_LEFT | MV_RIGHT
13136   };
13137   static int test_dir[4] =
13138   {
13139     MV_UP,
13140     MV_LEFT,
13141     MV_RIGHT,
13142     MV_DOWN
13143   };
13144
13145   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13146     return;
13147
13148   for (i = 0; i < NUM_DIRECTIONS; i++)
13149   {
13150     int test_x, test_y, test_move_dir, test_element;
13151
13152     test_x = bad_x + test_xy[i][0];
13153     test_y = bad_y + test_xy[i][1];
13154
13155     if (!IN_LEV_FIELD(test_x, test_y))
13156       continue;
13157
13158     test_move_dir =
13159       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13160
13161     test_element = Feld[test_x][test_y];
13162
13163     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13164        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13165     */
13166     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13167         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13168     {
13169       /* good thing is player or penguin that does not move away */
13170       if (IS_PLAYER(test_x, test_y))
13171       {
13172         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13173
13174         if (bad_element == EL_ROBOT && player->is_moving)
13175           continue;     /* robot does not kill player if he is moving */
13176
13177         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13178         {
13179           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13180             continue;           /* center and border element do not touch */
13181         }
13182
13183         kill_x = test_x;
13184         kill_y = test_y;
13185
13186         break;
13187       }
13188       else if (test_element == EL_PENGUIN)
13189       {
13190         kill_x = test_x;
13191         kill_y = test_y;
13192
13193         break;
13194       }
13195     }
13196   }
13197
13198   if (kill_x != -1 || kill_y != -1)
13199   {
13200     if (IS_PLAYER(kill_x, kill_y))
13201     {
13202       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13203
13204       if (player->shield_deadly_time_left > 0 &&
13205           !IS_INDESTRUCTIBLE(bad_element))
13206         Bang(bad_x, bad_y);
13207       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13208         KillPlayer(player);
13209     }
13210     else
13211       Bang(kill_x, kill_y);
13212   }
13213 }
13214
13215 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13216 {
13217   int bad_element = Feld[bad_x][bad_y];
13218   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13219   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13220   int test_x = bad_x + dx, test_y = bad_y + dy;
13221   int test_move_dir, test_element;
13222   int kill_x = -1, kill_y = -1;
13223
13224   if (!IN_LEV_FIELD(test_x, test_y))
13225     return;
13226
13227   test_move_dir =
13228     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13229
13230   test_element = Feld[test_x][test_y];
13231
13232   if (test_move_dir != bad_move_dir)
13233   {
13234     /* good thing can be player or penguin that does not move away */
13235     if (IS_PLAYER(test_x, test_y))
13236     {
13237       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13238
13239       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13240          player as being hit when he is moving towards the bad thing, because
13241          the "get hit by" condition would be lost after the player stops) */
13242       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13243         return;         /* player moves away from bad thing */
13244
13245       kill_x = test_x;
13246       kill_y = test_y;
13247     }
13248     else if (test_element == EL_PENGUIN)
13249     {
13250       kill_x = test_x;
13251       kill_y = test_y;
13252     }
13253   }
13254
13255   if (kill_x != -1 || kill_y != -1)
13256   {
13257     if (IS_PLAYER(kill_x, kill_y))
13258     {
13259       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13260
13261       if (player->shield_deadly_time_left > 0 &&
13262           !IS_INDESTRUCTIBLE(bad_element))
13263         Bang(bad_x, bad_y);
13264       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13265         KillPlayer(player);
13266     }
13267     else
13268       Bang(kill_x, kill_y);
13269   }
13270 }
13271
13272 void TestIfPlayerTouchesBadThing(int x, int y)
13273 {
13274   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13275 }
13276
13277 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13278 {
13279   TestIfGoodThingHitsBadThing(x, y, move_dir);
13280 }
13281
13282 void TestIfBadThingTouchesPlayer(int x, int y)
13283 {
13284   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13285 }
13286
13287 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13288 {
13289   TestIfBadThingHitsGoodThing(x, y, move_dir);
13290 }
13291
13292 void TestIfFriendTouchesBadThing(int x, int y)
13293 {
13294   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13295 }
13296
13297 void TestIfBadThingTouchesFriend(int x, int y)
13298 {
13299   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13300 }
13301
13302 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13303 {
13304   int i, kill_x = bad_x, kill_y = bad_y;
13305   static int xy[4][2] =
13306   {
13307     { 0, -1 },
13308     { -1, 0 },
13309     { +1, 0 },
13310     { 0, +1 }
13311   };
13312
13313   for (i = 0; i < NUM_DIRECTIONS; i++)
13314   {
13315     int x, y, element;
13316
13317     x = bad_x + xy[i][0];
13318     y = bad_y + xy[i][1];
13319     if (!IN_LEV_FIELD(x, y))
13320       continue;
13321
13322     element = Feld[x][y];
13323     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13324         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13325     {
13326       kill_x = x;
13327       kill_y = y;
13328       break;
13329     }
13330   }
13331
13332   if (kill_x != bad_x || kill_y != bad_y)
13333     Bang(bad_x, bad_y);
13334 }
13335
13336 void KillPlayer(struct PlayerInfo *player)
13337 {
13338   int jx = player->jx, jy = player->jy;
13339
13340   if (!player->active)
13341     return;
13342
13343 #if 0
13344   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13345          player->killed, player->active, player->reanimated);
13346 #endif
13347
13348   /* the following code was introduced to prevent an infinite loop when calling
13349      -> Bang()
13350      -> CheckTriggeredElementChangeExt()
13351      -> ExecuteCustomElementAction()
13352      -> KillPlayer()
13353      -> (infinitely repeating the above sequence of function calls)
13354      which occurs when killing the player while having a CE with the setting
13355      "kill player X when explosion of <player X>"; the solution using a new
13356      field "player->killed" was chosen for backwards compatibility, although
13357      clever use of the fields "player->active" etc. would probably also work */
13358 #if 1
13359   if (player->killed)
13360     return;
13361 #endif
13362
13363   player->killed = TRUE;
13364
13365   /* remove accessible field at the player's position */
13366   Feld[jx][jy] = EL_EMPTY;
13367
13368   /* deactivate shield (else Bang()/Explode() would not work right) */
13369   player->shield_normal_time_left = 0;
13370   player->shield_deadly_time_left = 0;
13371
13372 #if 0
13373   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13374          player->killed, player->active, player->reanimated);
13375 #endif
13376
13377   Bang(jx, jy);
13378
13379 #if 0
13380   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13381          player->killed, player->active, player->reanimated);
13382 #endif
13383
13384   if (player->reanimated)       /* killed player may have been reanimated */
13385     player->killed = player->reanimated = FALSE;
13386   else
13387     BuryPlayer(player);
13388 }
13389
13390 static void KillPlayerUnlessEnemyProtected(int x, int y)
13391 {
13392   if (!PLAYER_ENEMY_PROTECTED(x, y))
13393     KillPlayer(PLAYERINFO(x, y));
13394 }
13395
13396 static void KillPlayerUnlessExplosionProtected(int x, int y)
13397 {
13398   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13399     KillPlayer(PLAYERINFO(x, y));
13400 }
13401
13402 void BuryPlayer(struct PlayerInfo *player)
13403 {
13404   int jx = player->jx, jy = player->jy;
13405
13406   if (!player->active)
13407     return;
13408
13409   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13410   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13411
13412   player->GameOver = TRUE;
13413   RemovePlayer(player);
13414 }
13415
13416 void RemovePlayer(struct PlayerInfo *player)
13417 {
13418   int jx = player->jx, jy = player->jy;
13419   int i, found = FALSE;
13420
13421   player->present = FALSE;
13422   player->active = FALSE;
13423
13424   if (!ExplodeField[jx][jy])
13425     StorePlayer[jx][jy] = 0;
13426
13427   if (player->is_moving)
13428     TEST_DrawLevelField(player->last_jx, player->last_jy);
13429
13430   for (i = 0; i < MAX_PLAYERS; i++)
13431     if (stored_player[i].active)
13432       found = TRUE;
13433
13434   if (!found)
13435     AllPlayersGone = TRUE;
13436
13437   ExitX = ZX = jx;
13438   ExitY = ZY = jy;
13439 }
13440
13441 static void setFieldForSnapping(int x, int y, int element, int direction)
13442 {
13443   struct ElementInfo *ei = &element_info[element];
13444   int direction_bit = MV_DIR_TO_BIT(direction);
13445   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13446   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13447                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13448
13449   Feld[x][y] = EL_ELEMENT_SNAPPING;
13450   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13451
13452   ResetGfxAnimation(x, y);
13453
13454   GfxElement[x][y] = element;
13455   GfxAction[x][y] = action;
13456   GfxDir[x][y] = direction;
13457   GfxFrame[x][y] = -1;
13458 }
13459
13460 /*
13461   =============================================================================
13462   checkDiagonalPushing()
13463   -----------------------------------------------------------------------------
13464   check if diagonal input device direction results in pushing of object
13465   (by checking if the alternative direction is walkable, diggable, ...)
13466   =============================================================================
13467 */
13468
13469 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13470                                     int x, int y, int real_dx, int real_dy)
13471 {
13472   int jx, jy, dx, dy, xx, yy;
13473
13474   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13475     return TRUE;
13476
13477   /* diagonal direction: check alternative direction */
13478   jx = player->jx;
13479   jy = player->jy;
13480   dx = x - jx;
13481   dy = y - jy;
13482   xx = jx + (dx == 0 ? real_dx : 0);
13483   yy = jy + (dy == 0 ? real_dy : 0);
13484
13485   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13486 }
13487
13488 /*
13489   =============================================================================
13490   DigField()
13491   -----------------------------------------------------------------------------
13492   x, y:                 field next to player (non-diagonal) to try to dig to
13493   real_dx, real_dy:     direction as read from input device (can be diagonal)
13494   =============================================================================
13495 */
13496
13497 static int DigField(struct PlayerInfo *player,
13498                     int oldx, int oldy, int x, int y,
13499                     int real_dx, int real_dy, int mode)
13500 {
13501   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13502   boolean player_was_pushing = player->is_pushing;
13503   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13504   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13505   int jx = oldx, jy = oldy;
13506   int dx = x - jx, dy = y - jy;
13507   int nextx = x + dx, nexty = y + dy;
13508   int move_direction = (dx == -1 ? MV_LEFT  :
13509                         dx == +1 ? MV_RIGHT :
13510                         dy == -1 ? MV_UP    :
13511                         dy == +1 ? MV_DOWN  : MV_NONE);
13512   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13513   int dig_side = MV_DIR_OPPOSITE(move_direction);
13514   int old_element = Feld[jx][jy];
13515   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13516   int collect_count;
13517
13518   if (is_player)                /* function can also be called by EL_PENGUIN */
13519   {
13520     if (player->MovPos == 0)
13521     {
13522       player->is_digging = FALSE;
13523       player->is_collecting = FALSE;
13524     }
13525
13526     if (player->MovPos == 0)    /* last pushing move finished */
13527       player->is_pushing = FALSE;
13528
13529     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13530     {
13531       player->is_switching = FALSE;
13532       player->push_delay = -1;
13533
13534       return MP_NO_ACTION;
13535     }
13536   }
13537
13538   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13539     old_element = Back[jx][jy];
13540
13541   /* in case of element dropped at player position, check background */
13542   else if (Back[jx][jy] != EL_EMPTY &&
13543            game.engine_version >= VERSION_IDENT(2,2,0,0))
13544     old_element = Back[jx][jy];
13545
13546   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13547     return MP_NO_ACTION;        /* field has no opening in this direction */
13548
13549   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13550     return MP_NO_ACTION;        /* field has no opening in this direction */
13551
13552   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13553   {
13554     SplashAcid(x, y);
13555
13556     Feld[jx][jy] = player->artwork_element;
13557     InitMovingField(jx, jy, MV_DOWN);
13558     Store[jx][jy] = EL_ACID;
13559     ContinueMoving(jx, jy);
13560     BuryPlayer(player);
13561
13562     return MP_DONT_RUN_INTO;
13563   }
13564
13565   if (player_can_move && DONT_RUN_INTO(element))
13566   {
13567     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13568
13569     return MP_DONT_RUN_INTO;
13570   }
13571
13572   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13573     return MP_NO_ACTION;
13574
13575   collect_count = element_info[element].collect_count_initial;
13576
13577   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13578     return MP_NO_ACTION;
13579
13580   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13581     player_can_move = player_can_move_or_snap;
13582
13583   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13584       game.engine_version >= VERSION_IDENT(2,2,0,0))
13585   {
13586     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13587                                player->index_bit, dig_side);
13588     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13589                                         player->index_bit, dig_side);
13590
13591     if (element == EL_DC_LANDMINE)
13592       Bang(x, y);
13593
13594     if (Feld[x][y] != element)          /* field changed by snapping */
13595       return MP_ACTION;
13596
13597     return MP_NO_ACTION;
13598   }
13599
13600   if (player->gravity && is_player && !player->is_auto_moving &&
13601       canFallDown(player) && move_direction != MV_DOWN &&
13602       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13603     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13604
13605   if (player_can_move &&
13606       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13607   {
13608     int sound_element = SND_ELEMENT(element);
13609     int sound_action = ACTION_WALKING;
13610
13611     if (IS_RND_GATE(element))
13612     {
13613       if (!player->key[RND_GATE_NR(element)])
13614         return MP_NO_ACTION;
13615     }
13616     else if (IS_RND_GATE_GRAY(element))
13617     {
13618       if (!player->key[RND_GATE_GRAY_NR(element)])
13619         return MP_NO_ACTION;
13620     }
13621     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13622     {
13623       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13624         return MP_NO_ACTION;
13625     }
13626     else if (element == EL_EXIT_OPEN ||
13627              element == EL_EM_EXIT_OPEN ||
13628              element == EL_EM_EXIT_OPENING ||
13629              element == EL_STEEL_EXIT_OPEN ||
13630              element == EL_EM_STEEL_EXIT_OPEN ||
13631              element == EL_EM_STEEL_EXIT_OPENING ||
13632              element == EL_SP_EXIT_OPEN ||
13633              element == EL_SP_EXIT_OPENING)
13634     {
13635       sound_action = ACTION_PASSING;    /* player is passing exit */
13636     }
13637     else if (element == EL_EMPTY)
13638     {
13639       sound_action = ACTION_MOVING;             /* nothing to walk on */
13640     }
13641
13642     /* play sound from background or player, whatever is available */
13643     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13644       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13645     else
13646       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13647   }
13648   else if (player_can_move &&
13649            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13650   {
13651     if (!ACCESS_FROM(element, opposite_direction))
13652       return MP_NO_ACTION;      /* field not accessible from this direction */
13653
13654     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13655       return MP_NO_ACTION;
13656
13657     if (IS_EM_GATE(element))
13658     {
13659       if (!player->key[EM_GATE_NR(element)])
13660         return MP_NO_ACTION;
13661     }
13662     else if (IS_EM_GATE_GRAY(element))
13663     {
13664       if (!player->key[EM_GATE_GRAY_NR(element)])
13665         return MP_NO_ACTION;
13666     }
13667     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13668     {
13669       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13670         return MP_NO_ACTION;
13671     }
13672     else if (IS_EMC_GATE(element))
13673     {
13674       if (!player->key[EMC_GATE_NR(element)])
13675         return MP_NO_ACTION;
13676     }
13677     else if (IS_EMC_GATE_GRAY(element))
13678     {
13679       if (!player->key[EMC_GATE_GRAY_NR(element)])
13680         return MP_NO_ACTION;
13681     }
13682     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13683     {
13684       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13685         return MP_NO_ACTION;
13686     }
13687     else if (element == EL_DC_GATE_WHITE ||
13688              element == EL_DC_GATE_WHITE_GRAY ||
13689              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13690     {
13691       if (player->num_white_keys == 0)
13692         return MP_NO_ACTION;
13693
13694       player->num_white_keys--;
13695     }
13696     else if (IS_SP_PORT(element))
13697     {
13698       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13699           element == EL_SP_GRAVITY_PORT_RIGHT ||
13700           element == EL_SP_GRAVITY_PORT_UP ||
13701           element == EL_SP_GRAVITY_PORT_DOWN)
13702         player->gravity = !player->gravity;
13703       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13704                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13705                element == EL_SP_GRAVITY_ON_PORT_UP ||
13706                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13707         player->gravity = TRUE;
13708       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13709                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13710                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13711                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13712         player->gravity = FALSE;
13713     }
13714
13715     /* automatically move to the next field with double speed */
13716     player->programmed_action = move_direction;
13717
13718     if (player->move_delay_reset_counter == 0)
13719     {
13720       player->move_delay_reset_counter = 2;     /* two double speed steps */
13721
13722       DOUBLE_PLAYER_SPEED(player);
13723     }
13724
13725     PlayLevelSoundAction(x, y, ACTION_PASSING);
13726   }
13727   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13728   {
13729     RemoveField(x, y);
13730
13731     if (mode != DF_SNAP)
13732     {
13733       GfxElement[x][y] = GFX_ELEMENT(element);
13734       player->is_digging = TRUE;
13735     }
13736
13737     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13738
13739     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13740                                         player->index_bit, dig_side);
13741
13742     if (mode == DF_SNAP)
13743     {
13744       if (level.block_snap_field)
13745         setFieldForSnapping(x, y, element, move_direction);
13746       else
13747         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13748
13749       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13750                                           player->index_bit, dig_side);
13751     }
13752   }
13753   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13754   {
13755     RemoveField(x, y);
13756
13757     if (is_player && mode != DF_SNAP)
13758     {
13759       GfxElement[x][y] = element;
13760       player->is_collecting = TRUE;
13761     }
13762
13763     if (element == EL_SPEED_PILL)
13764     {
13765       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13766     }
13767     else if (element == EL_EXTRA_TIME && level.time > 0)
13768     {
13769       TimeLeft += level.extra_time;
13770
13771       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13772
13773       DisplayGameControlValues();
13774     }
13775     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13776     {
13777       player->shield_normal_time_left += level.shield_normal_time;
13778       if (element == EL_SHIELD_DEADLY)
13779         player->shield_deadly_time_left += level.shield_deadly_time;
13780     }
13781     else if (element == EL_DYNAMITE ||
13782              element == EL_EM_DYNAMITE ||
13783              element == EL_SP_DISK_RED)
13784     {
13785       if (player->inventory_size < MAX_INVENTORY_SIZE)
13786         player->inventory_element[player->inventory_size++] = element;
13787
13788       DrawGameDoorValues();
13789     }
13790     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13791     {
13792       player->dynabomb_count++;
13793       player->dynabombs_left++;
13794     }
13795     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13796     {
13797       player->dynabomb_size++;
13798     }
13799     else if (element == EL_DYNABOMB_INCREASE_POWER)
13800     {
13801       player->dynabomb_xl = TRUE;
13802     }
13803     else if (IS_KEY(element))
13804     {
13805       player->key[KEY_NR(element)] = TRUE;
13806
13807       DrawGameDoorValues();
13808     }
13809     else if (element == EL_DC_KEY_WHITE)
13810     {
13811       player->num_white_keys++;
13812
13813       /* display white keys? */
13814       /* DrawGameDoorValues(); */
13815     }
13816     else if (IS_ENVELOPE(element))
13817     {
13818       player->show_envelope = element;
13819     }
13820     else if (element == EL_EMC_LENSES)
13821     {
13822       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13823
13824       RedrawAllInvisibleElementsForLenses();
13825     }
13826     else if (element == EL_EMC_MAGNIFIER)
13827     {
13828       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13829
13830       RedrawAllInvisibleElementsForMagnifier();
13831     }
13832     else if (IS_DROPPABLE(element) ||
13833              IS_THROWABLE(element))     /* can be collected and dropped */
13834     {
13835       int i;
13836
13837       if (collect_count == 0)
13838         player->inventory_infinite_element = element;
13839       else
13840         for (i = 0; i < collect_count; i++)
13841           if (player->inventory_size < MAX_INVENTORY_SIZE)
13842             player->inventory_element[player->inventory_size++] = element;
13843
13844       DrawGameDoorValues();
13845     }
13846     else if (collect_count > 0)
13847     {
13848       local_player->gems_still_needed -= collect_count;
13849       if (local_player->gems_still_needed < 0)
13850         local_player->gems_still_needed = 0;
13851
13852       game.snapshot.collected_item = TRUE;
13853
13854       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13855
13856       DisplayGameControlValues();
13857     }
13858
13859     RaiseScoreElement(element);
13860     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13861
13862     if (is_player)
13863       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13864                                           player->index_bit, dig_side);
13865
13866     if (mode == DF_SNAP)
13867     {
13868       if (level.block_snap_field)
13869         setFieldForSnapping(x, y, element, move_direction);
13870       else
13871         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13872
13873       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13874                                           player->index_bit, dig_side);
13875     }
13876   }
13877   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13878   {
13879     if (mode == DF_SNAP && element != EL_BD_ROCK)
13880       return MP_NO_ACTION;
13881
13882     if (CAN_FALL(element) && dy)
13883       return MP_NO_ACTION;
13884
13885     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13886         !(element == EL_SPRING && level.use_spring_bug))
13887       return MP_NO_ACTION;
13888
13889     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13890         ((move_direction & MV_VERTICAL &&
13891           ((element_info[element].move_pattern & MV_LEFT &&
13892             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13893            (element_info[element].move_pattern & MV_RIGHT &&
13894             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13895          (move_direction & MV_HORIZONTAL &&
13896           ((element_info[element].move_pattern & MV_UP &&
13897             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13898            (element_info[element].move_pattern & MV_DOWN &&
13899             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13900       return MP_NO_ACTION;
13901
13902     /* do not push elements already moving away faster than player */
13903     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13904         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13905       return MP_NO_ACTION;
13906
13907     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13908     {
13909       if (player->push_delay_value == -1 || !player_was_pushing)
13910         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13911     }
13912     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13913     {
13914       if (player->push_delay_value == -1)
13915         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13916     }
13917     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13918     {
13919       if (!player->is_pushing)
13920         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13921     }
13922
13923     player->is_pushing = TRUE;
13924     player->is_active = TRUE;
13925
13926     if (!(IN_LEV_FIELD(nextx, nexty) &&
13927           (IS_FREE(nextx, nexty) ||
13928            (IS_SB_ELEMENT(element) &&
13929             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13930            (IS_CUSTOM_ELEMENT(element) &&
13931             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13932       return MP_NO_ACTION;
13933
13934     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13935       return MP_NO_ACTION;
13936
13937     if (player->push_delay == -1)       /* new pushing; restart delay */
13938       player->push_delay = 0;
13939
13940     if (player->push_delay < player->push_delay_value &&
13941         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13942         element != EL_SPRING && element != EL_BALLOON)
13943     {
13944       /* make sure that there is no move delay before next try to push */
13945       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13946         player->move_delay = 0;
13947
13948       return MP_NO_ACTION;
13949     }
13950
13951     if (IS_CUSTOM_ELEMENT(element) &&
13952         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13953     {
13954       if (!DigFieldByCE(nextx, nexty, element))
13955         return MP_NO_ACTION;
13956     }
13957
13958     if (IS_SB_ELEMENT(element))
13959     {
13960       if (element == EL_SOKOBAN_FIELD_FULL)
13961       {
13962         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13963         local_player->sokobanfields_still_needed++;
13964       }
13965
13966       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13967       {
13968         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13969         local_player->sokobanfields_still_needed--;
13970       }
13971
13972       Feld[x][y] = EL_SOKOBAN_OBJECT;
13973
13974       if (Back[x][y] == Back[nextx][nexty])
13975         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13976       else if (Back[x][y] != 0)
13977         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13978                                     ACTION_EMPTYING);
13979       else
13980         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13981                                     ACTION_FILLING);
13982
13983       if (local_player->sokobanfields_still_needed == 0 &&
13984           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13985       {
13986         PlayerWins(player);
13987
13988         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13989       }
13990     }
13991     else
13992       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13993
13994     InitMovingField(x, y, move_direction);
13995     GfxAction[x][y] = ACTION_PUSHING;
13996
13997     if (mode == DF_SNAP)
13998       ContinueMoving(x, y);
13999     else
14000       MovPos[x][y] = (dx != 0 ? dx : dy);
14001
14002     Pushed[x][y] = TRUE;
14003     Pushed[nextx][nexty] = TRUE;
14004
14005     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14006       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14007     else
14008       player->push_delay_value = -1;    /* get new value later */
14009
14010     /* check for element change _after_ element has been pushed */
14011     if (game.use_change_when_pushing_bug)
14012     {
14013       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14014                                  player->index_bit, dig_side);
14015       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14016                                           player->index_bit, dig_side);
14017     }
14018   }
14019   else if (IS_SWITCHABLE(element))
14020   {
14021     if (PLAYER_SWITCHING(player, x, y))
14022     {
14023       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14024                                           player->index_bit, dig_side);
14025
14026       return MP_ACTION;
14027     }
14028
14029     player->is_switching = TRUE;
14030     player->switch_x = x;
14031     player->switch_y = y;
14032
14033     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14034
14035     if (element == EL_ROBOT_WHEEL)
14036     {
14037       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14038       ZX = x;
14039       ZY = y;
14040
14041       game.robot_wheel_active = TRUE;
14042
14043       TEST_DrawLevelField(x, y);
14044     }
14045     else if (element == EL_SP_TERMINAL)
14046     {
14047       int xx, yy;
14048
14049       SCAN_PLAYFIELD(xx, yy)
14050       {
14051         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14052         {
14053           Bang(xx, yy);
14054         }
14055         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14056         {
14057           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14058
14059           ResetGfxAnimation(xx, yy);
14060           TEST_DrawLevelField(xx, yy);
14061         }
14062       }
14063     }
14064     else if (IS_BELT_SWITCH(element))
14065     {
14066       ToggleBeltSwitch(x, y);
14067     }
14068     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14069              element == EL_SWITCHGATE_SWITCH_DOWN ||
14070              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14071              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14072     {
14073       ToggleSwitchgateSwitch(x, y);
14074     }
14075     else if (element == EL_LIGHT_SWITCH ||
14076              element == EL_LIGHT_SWITCH_ACTIVE)
14077     {
14078       ToggleLightSwitch(x, y);
14079     }
14080     else if (element == EL_TIMEGATE_SWITCH ||
14081              element == EL_DC_TIMEGATE_SWITCH)
14082     {
14083       ActivateTimegateSwitch(x, y);
14084     }
14085     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14086              element == EL_BALLOON_SWITCH_RIGHT ||
14087              element == EL_BALLOON_SWITCH_UP    ||
14088              element == EL_BALLOON_SWITCH_DOWN  ||
14089              element == EL_BALLOON_SWITCH_NONE  ||
14090              element == EL_BALLOON_SWITCH_ANY)
14091     {
14092       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14093                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14094                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14095                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14096                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14097                              move_direction);
14098     }
14099     else if (element == EL_LAMP)
14100     {
14101       Feld[x][y] = EL_LAMP_ACTIVE;
14102       local_player->lights_still_needed--;
14103
14104       ResetGfxAnimation(x, y);
14105       TEST_DrawLevelField(x, y);
14106     }
14107     else if (element == EL_TIME_ORB_FULL)
14108     {
14109       Feld[x][y] = EL_TIME_ORB_EMPTY;
14110
14111       if (level.time > 0 || level.use_time_orb_bug)
14112       {
14113         TimeLeft += level.time_orb_time;
14114         game.no_time_limit = FALSE;
14115
14116         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14117
14118         DisplayGameControlValues();
14119       }
14120
14121       ResetGfxAnimation(x, y);
14122       TEST_DrawLevelField(x, y);
14123     }
14124     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14125              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14126     {
14127       int xx, yy;
14128
14129       game.ball_state = !game.ball_state;
14130
14131       SCAN_PLAYFIELD(xx, yy)
14132       {
14133         int e = Feld[xx][yy];
14134
14135         if (game.ball_state)
14136         {
14137           if (e == EL_EMC_MAGIC_BALL)
14138             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14139           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14140             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14141         }
14142         else
14143         {
14144           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14145             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14146           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14147             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14148         }
14149       }
14150     }
14151
14152     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14153                                         player->index_bit, dig_side);
14154
14155     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14156                                         player->index_bit, dig_side);
14157
14158     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14159                                         player->index_bit, dig_side);
14160
14161     return MP_ACTION;
14162   }
14163   else
14164   {
14165     if (!PLAYER_SWITCHING(player, x, y))
14166     {
14167       player->is_switching = TRUE;
14168       player->switch_x = x;
14169       player->switch_y = y;
14170
14171       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14172                                  player->index_bit, dig_side);
14173       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14174                                           player->index_bit, dig_side);
14175
14176       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14177                                  player->index_bit, dig_side);
14178       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14179                                           player->index_bit, dig_side);
14180     }
14181
14182     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14183                                player->index_bit, dig_side);
14184     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14185                                         player->index_bit, dig_side);
14186
14187     return MP_NO_ACTION;
14188   }
14189
14190   player->push_delay = -1;
14191
14192   if (is_player)                /* function can also be called by EL_PENGUIN */
14193   {
14194     if (Feld[x][y] != element)          /* really digged/collected something */
14195     {
14196       player->is_collecting = !player->is_digging;
14197       player->is_active = TRUE;
14198     }
14199   }
14200
14201   return MP_MOVING;
14202 }
14203
14204 static boolean DigFieldByCE(int x, int y, int digging_element)
14205 {
14206   int element = Feld[x][y];
14207
14208   if (!IS_FREE(x, y))
14209   {
14210     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14211                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14212                   ACTION_BREAKING);
14213
14214     /* no element can dig solid indestructible elements */
14215     if (IS_INDESTRUCTIBLE(element) &&
14216         !IS_DIGGABLE(element) &&
14217         !IS_COLLECTIBLE(element))
14218       return FALSE;
14219
14220     if (AmoebaNr[x][y] &&
14221         (element == EL_AMOEBA_FULL ||
14222          element == EL_BD_AMOEBA ||
14223          element == EL_AMOEBA_GROWING))
14224     {
14225       AmoebaCnt[AmoebaNr[x][y]]--;
14226       AmoebaCnt2[AmoebaNr[x][y]]--;
14227     }
14228
14229     if (IS_MOVING(x, y))
14230       RemoveMovingField(x, y);
14231     else
14232     {
14233       RemoveField(x, y);
14234       TEST_DrawLevelField(x, y);
14235     }
14236
14237     /* if digged element was about to explode, prevent the explosion */
14238     ExplodeField[x][y] = EX_TYPE_NONE;
14239
14240     PlayLevelSoundAction(x, y, action);
14241   }
14242
14243   Store[x][y] = EL_EMPTY;
14244
14245   /* this makes it possible to leave the removed element again */
14246   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14247     Store[x][y] = element;
14248
14249   return TRUE;
14250 }
14251
14252 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14253 {
14254   int jx = player->jx, jy = player->jy;
14255   int x = jx + dx, y = jy + dy;
14256   int snap_direction = (dx == -1 ? MV_LEFT  :
14257                         dx == +1 ? MV_RIGHT :
14258                         dy == -1 ? MV_UP    :
14259                         dy == +1 ? MV_DOWN  : MV_NONE);
14260   boolean can_continue_snapping = (level.continuous_snapping &&
14261                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14262
14263   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14264     return FALSE;
14265
14266   if (!player->active || !IN_LEV_FIELD(x, y))
14267     return FALSE;
14268
14269   if (dx && dy)
14270     return FALSE;
14271
14272   if (!dx && !dy)
14273   {
14274     if (player->MovPos == 0)
14275       player->is_pushing = FALSE;
14276
14277     player->is_snapping = FALSE;
14278
14279     if (player->MovPos == 0)
14280     {
14281       player->is_moving = FALSE;
14282       player->is_digging = FALSE;
14283       player->is_collecting = FALSE;
14284     }
14285
14286     return FALSE;
14287   }
14288
14289   /* prevent snapping with already pressed snap key when not allowed */
14290   if (player->is_snapping && !can_continue_snapping)
14291     return FALSE;
14292
14293   player->MovDir = snap_direction;
14294
14295   if (player->MovPos == 0)
14296   {
14297     player->is_moving = FALSE;
14298     player->is_digging = FALSE;
14299     player->is_collecting = FALSE;
14300   }
14301
14302   player->is_dropping = FALSE;
14303   player->is_dropping_pressed = FALSE;
14304   player->drop_pressed_delay = 0;
14305
14306   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14307     return FALSE;
14308
14309   player->is_snapping = TRUE;
14310   player->is_active = TRUE;
14311
14312   if (player->MovPos == 0)
14313   {
14314     player->is_moving = FALSE;
14315     player->is_digging = FALSE;
14316     player->is_collecting = FALSE;
14317   }
14318
14319   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14320     TEST_DrawLevelField(player->last_jx, player->last_jy);
14321
14322   TEST_DrawLevelField(x, y);
14323
14324   return TRUE;
14325 }
14326
14327 static boolean DropElement(struct PlayerInfo *player)
14328 {
14329   int old_element, new_element;
14330   int dropx = player->jx, dropy = player->jy;
14331   int drop_direction = player->MovDir;
14332   int drop_side = drop_direction;
14333   int drop_element = get_next_dropped_element(player);
14334
14335   /* do not drop an element on top of another element; when holding drop key
14336      pressed without moving, dropped element must move away before the next
14337      element can be dropped (this is especially important if the next element
14338      is dynamite, which can be placed on background for historical reasons) */
14339   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14340     return MP_ACTION;
14341
14342   if (IS_THROWABLE(drop_element))
14343   {
14344     dropx += GET_DX_FROM_DIR(drop_direction);
14345     dropy += GET_DY_FROM_DIR(drop_direction);
14346
14347     if (!IN_LEV_FIELD(dropx, dropy))
14348       return FALSE;
14349   }
14350
14351   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14352   new_element = drop_element;           /* default: no change when dropping */
14353
14354   /* check if player is active, not moving and ready to drop */
14355   if (!player->active || player->MovPos || player->drop_delay > 0)
14356     return FALSE;
14357
14358   /* check if player has anything that can be dropped */
14359   if (new_element == EL_UNDEFINED)
14360     return FALSE;
14361
14362   /* only set if player has anything that can be dropped */
14363   player->is_dropping_pressed = TRUE;
14364
14365   /* check if drop key was pressed long enough for EM style dynamite */
14366   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14367     return FALSE;
14368
14369   /* check if anything can be dropped at the current position */
14370   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14371     return FALSE;
14372
14373   /* collected custom elements can only be dropped on empty fields */
14374   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14375     return FALSE;
14376
14377   if (old_element != EL_EMPTY)
14378     Back[dropx][dropy] = old_element;   /* store old element on this field */
14379
14380   ResetGfxAnimation(dropx, dropy);
14381   ResetRandomAnimationValue(dropx, dropy);
14382
14383   if (player->inventory_size > 0 ||
14384       player->inventory_infinite_element != EL_UNDEFINED)
14385   {
14386     if (player->inventory_size > 0)
14387     {
14388       player->inventory_size--;
14389
14390       DrawGameDoorValues();
14391
14392       if (new_element == EL_DYNAMITE)
14393         new_element = EL_DYNAMITE_ACTIVE;
14394       else if (new_element == EL_EM_DYNAMITE)
14395         new_element = EL_EM_DYNAMITE_ACTIVE;
14396       else if (new_element == EL_SP_DISK_RED)
14397         new_element = EL_SP_DISK_RED_ACTIVE;
14398     }
14399
14400     Feld[dropx][dropy] = new_element;
14401
14402     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14403       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14404                           el2img(Feld[dropx][dropy]), 0);
14405
14406     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14407
14408     /* needed if previous element just changed to "empty" in the last frame */
14409     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14410
14411     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14412                                player->index_bit, drop_side);
14413     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14414                                         CE_PLAYER_DROPS_X,
14415                                         player->index_bit, drop_side);
14416
14417     TestIfElementTouchesCustomElement(dropx, dropy);
14418   }
14419   else          /* player is dropping a dyna bomb */
14420   {
14421     player->dynabombs_left--;
14422
14423     Feld[dropx][dropy] = new_element;
14424
14425     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14426       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14427                           el2img(Feld[dropx][dropy]), 0);
14428
14429     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14430   }
14431
14432   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14433     InitField_WithBug1(dropx, dropy, FALSE);
14434
14435   new_element = Feld[dropx][dropy];     /* element might have changed */
14436
14437   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14438       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14439   {
14440     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14441       MovDir[dropx][dropy] = drop_direction;
14442
14443     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14444
14445     /* do not cause impact style collision by dropping elements that can fall */
14446     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14447   }
14448
14449   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14450   player->is_dropping = TRUE;
14451
14452   player->drop_pressed_delay = 0;
14453   player->is_dropping_pressed = FALSE;
14454
14455   player->drop_x = dropx;
14456   player->drop_y = dropy;
14457
14458   return TRUE;
14459 }
14460
14461 /* ------------------------------------------------------------------------- */
14462 /* game sound playing functions                                              */
14463 /* ------------------------------------------------------------------------- */
14464
14465 static int *loop_sound_frame = NULL;
14466 static int *loop_sound_volume = NULL;
14467
14468 void InitPlayLevelSound()
14469 {
14470   int num_sounds = getSoundListSize();
14471
14472   checked_free(loop_sound_frame);
14473   checked_free(loop_sound_volume);
14474
14475   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14476   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14477 }
14478
14479 static void PlayLevelSound(int x, int y, int nr)
14480 {
14481   int sx = SCREENX(x), sy = SCREENY(y);
14482   int volume, stereo_position;
14483   int max_distance = 8;
14484   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14485
14486   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14487       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14488     return;
14489
14490   if (!IN_LEV_FIELD(x, y) ||
14491       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14492       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14493     return;
14494
14495   volume = SOUND_MAX_VOLUME;
14496
14497   if (!IN_SCR_FIELD(sx, sy))
14498   {
14499     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14500     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14501
14502     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14503   }
14504
14505   stereo_position = (SOUND_MAX_LEFT +
14506                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14507                      (SCR_FIELDX + 2 * max_distance));
14508
14509   if (IS_LOOP_SOUND(nr))
14510   {
14511     /* This assures that quieter loop sounds do not overwrite louder ones,
14512        while restarting sound volume comparison with each new game frame. */
14513
14514     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14515       return;
14516
14517     loop_sound_volume[nr] = volume;
14518     loop_sound_frame[nr] = FrameCounter;
14519   }
14520
14521   PlaySoundExt(nr, volume, stereo_position, type);
14522 }
14523
14524 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14525 {
14526   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14527                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14528                  y < LEVELY(BY1) ? LEVELY(BY1) :
14529                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14530                  sound_action);
14531 }
14532
14533 static void PlayLevelSoundAction(int x, int y, int action)
14534 {
14535   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14536 }
14537
14538 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14539 {
14540   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14541
14542   if (sound_effect != SND_UNDEFINED)
14543     PlayLevelSound(x, y, sound_effect);
14544 }
14545
14546 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14547                                               int action)
14548 {
14549   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14550
14551   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14552     PlayLevelSound(x, y, sound_effect);
14553 }
14554
14555 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14556 {
14557   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14558
14559   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14560     PlayLevelSound(x, y, sound_effect);
14561 }
14562
14563 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14564 {
14565   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14566
14567   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14568     StopSound(sound_effect);
14569 }
14570
14571 static int getLevelMusicNr()
14572 {
14573   if (levelset.music[level_nr] != MUS_UNDEFINED)
14574     return levelset.music[level_nr];            /* from config file */
14575   else
14576     return MAP_NOCONF_MUSIC(level_nr);          /* from music dir */
14577 }
14578
14579 static void FadeLevelSounds()
14580 {
14581   FadeSounds();
14582 }
14583
14584 static void FadeLevelMusic()
14585 {
14586   int music_nr = getLevelMusicNr();
14587   char *curr_music = getCurrentlyPlayingMusicFilename();
14588   char *next_music = getMusicInfoEntryFilename(music_nr);
14589
14590   if (!strEqual(curr_music, next_music))
14591     FadeMusic();
14592 }
14593
14594 void FadeLevelSoundsAndMusic()
14595 {
14596   FadeLevelSounds();
14597   FadeLevelMusic();
14598 }
14599
14600 static void PlayLevelMusic()
14601 {
14602   int music_nr = getLevelMusicNr();
14603   char *curr_music = getCurrentlyPlayingMusicFilename();
14604   char *next_music = getMusicInfoEntryFilename(music_nr);
14605
14606   if (!strEqual(curr_music, next_music))
14607     PlayMusic(music_nr);
14608 }
14609
14610 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14611 {
14612   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14613   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14614   int x = xx - 1 - offset;
14615   int y = yy - 1 - offset;
14616
14617   switch (sample)
14618   {
14619     case SAMPLE_blank:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14621       break;
14622
14623     case SAMPLE_roll:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14625       break;
14626
14627     case SAMPLE_stone:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14629       break;
14630
14631     case SAMPLE_nut:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14633       break;
14634
14635     case SAMPLE_crack:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14637       break;
14638
14639     case SAMPLE_bug:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14641       break;
14642
14643     case SAMPLE_tank:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14645       break;
14646
14647     case SAMPLE_android_clone:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14649       break;
14650
14651     case SAMPLE_android_move:
14652       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14653       break;
14654
14655     case SAMPLE_spring:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14657       break;
14658
14659     case SAMPLE_slurp:
14660       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14661       break;
14662
14663     case SAMPLE_eater:
14664       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14665       break;
14666
14667     case SAMPLE_eater_eat:
14668       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14669       break;
14670
14671     case SAMPLE_alien:
14672       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14673       break;
14674
14675     case SAMPLE_collect:
14676       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14677       break;
14678
14679     case SAMPLE_diamond:
14680       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14681       break;
14682
14683     case SAMPLE_squash:
14684       /* !!! CHECK THIS !!! */
14685 #if 1
14686       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14687 #else
14688       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14689 #endif
14690       break;
14691
14692     case SAMPLE_wonderfall:
14693       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14694       break;
14695
14696     case SAMPLE_drip:
14697       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14698       break;
14699
14700     case SAMPLE_push:
14701       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14702       break;
14703
14704     case SAMPLE_dirt:
14705       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14706       break;
14707
14708     case SAMPLE_acid:
14709       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14710       break;
14711
14712     case SAMPLE_ball:
14713       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14714       break;
14715
14716     case SAMPLE_grow:
14717       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14718       break;
14719
14720     case SAMPLE_wonder:
14721       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14722       break;
14723
14724     case SAMPLE_door:
14725       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14726       break;
14727
14728     case SAMPLE_exit_open:
14729       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14730       break;
14731
14732     case SAMPLE_exit_leave:
14733       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14734       break;
14735
14736     case SAMPLE_dynamite:
14737       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14738       break;
14739
14740     case SAMPLE_tick:
14741       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14742       break;
14743
14744     case SAMPLE_press:
14745       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14746       break;
14747
14748     case SAMPLE_wheel:
14749       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14750       break;
14751
14752     case SAMPLE_boom:
14753       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14754       break;
14755
14756     case SAMPLE_die:
14757       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14758       break;
14759
14760     case SAMPLE_time:
14761       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14762       break;
14763
14764     default:
14765       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14766       break;
14767   }
14768 }
14769
14770 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14771 {
14772   int element = map_element_SP_to_RND(element_sp);
14773   int action = map_action_SP_to_RND(action_sp);
14774   int offset = (setup.sp_show_border_elements ? 0 : 1);
14775   int x = xx - offset;
14776   int y = yy - offset;
14777
14778   PlayLevelSoundElementAction(x, y, element, action);
14779 }
14780
14781 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
14782 {
14783   int element = map_element_MM_to_RND(element_mm);
14784   int action = map_action_MM_to_RND(action_mm);
14785   int offset = 0;
14786   int x = xx - offset;
14787   int y = yy - offset;
14788
14789   if (!IS_MM_ELEMENT(element))
14790     element = EL_MM_DEFAULT;
14791
14792   PlayLevelSoundElementAction(x, y, element, action);
14793 }
14794
14795 void PlaySound_MM(int sound_mm)
14796 {
14797   int sound = map_sound_MM_to_RND(sound_mm);
14798
14799   if (sound == SND_UNDEFINED)
14800     return;
14801
14802   PlaySound(sound);
14803 }
14804
14805 void PlaySoundLoop_MM(int sound_mm)
14806 {
14807   int sound = map_sound_MM_to_RND(sound_mm);
14808
14809   if (sound == SND_UNDEFINED)
14810     return;
14811
14812   PlaySoundLoop(sound);
14813 }
14814
14815 void StopSound_MM(int sound_mm)
14816 {
14817   int sound = map_sound_MM_to_RND(sound_mm);
14818
14819   if (sound == SND_UNDEFINED)
14820     return;
14821
14822   StopSound(sound);
14823 }
14824
14825 void RaiseScore(int value)
14826 {
14827   local_player->score += value;
14828
14829   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14830
14831   DisplayGameControlValues();
14832 }
14833
14834 void RaiseScoreElement(int element)
14835 {
14836   switch (element)
14837   {
14838     case EL_EMERALD:
14839     case EL_BD_DIAMOND:
14840     case EL_EMERALD_YELLOW:
14841     case EL_EMERALD_RED:
14842     case EL_EMERALD_PURPLE:
14843     case EL_SP_INFOTRON:
14844       RaiseScore(level.score[SC_EMERALD]);
14845       break;
14846     case EL_DIAMOND:
14847       RaiseScore(level.score[SC_DIAMOND]);
14848       break;
14849     case EL_CRYSTAL:
14850       RaiseScore(level.score[SC_CRYSTAL]);
14851       break;
14852     case EL_PEARL:
14853       RaiseScore(level.score[SC_PEARL]);
14854       break;
14855     case EL_BUG:
14856     case EL_BD_BUTTERFLY:
14857     case EL_SP_ELECTRON:
14858       RaiseScore(level.score[SC_BUG]);
14859       break;
14860     case EL_SPACESHIP:
14861     case EL_BD_FIREFLY:
14862     case EL_SP_SNIKSNAK:
14863       RaiseScore(level.score[SC_SPACESHIP]);
14864       break;
14865     case EL_YAMYAM:
14866     case EL_DARK_YAMYAM:
14867       RaiseScore(level.score[SC_YAMYAM]);
14868       break;
14869     case EL_ROBOT:
14870       RaiseScore(level.score[SC_ROBOT]);
14871       break;
14872     case EL_PACMAN:
14873       RaiseScore(level.score[SC_PACMAN]);
14874       break;
14875     case EL_NUT:
14876       RaiseScore(level.score[SC_NUT]);
14877       break;
14878     case EL_DYNAMITE:
14879     case EL_EM_DYNAMITE:
14880     case EL_SP_DISK_RED:
14881     case EL_DYNABOMB_INCREASE_NUMBER:
14882     case EL_DYNABOMB_INCREASE_SIZE:
14883     case EL_DYNABOMB_INCREASE_POWER:
14884       RaiseScore(level.score[SC_DYNAMITE]);
14885       break;
14886     case EL_SHIELD_NORMAL:
14887     case EL_SHIELD_DEADLY:
14888       RaiseScore(level.score[SC_SHIELD]);
14889       break;
14890     case EL_EXTRA_TIME:
14891       RaiseScore(level.extra_time_score);
14892       break;
14893     case EL_KEY_1:
14894     case EL_KEY_2:
14895     case EL_KEY_3:
14896     case EL_KEY_4:
14897     case EL_EM_KEY_1:
14898     case EL_EM_KEY_2:
14899     case EL_EM_KEY_3:
14900     case EL_EM_KEY_4:
14901     case EL_EMC_KEY_5:
14902     case EL_EMC_KEY_6:
14903     case EL_EMC_KEY_7:
14904     case EL_EMC_KEY_8:
14905     case EL_DC_KEY_WHITE:
14906       RaiseScore(level.score[SC_KEY]);
14907       break;
14908     default:
14909       RaiseScore(element_info[element].collect_score);
14910       break;
14911   }
14912 }
14913
14914 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14915 {
14916   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14917   {
14918     /* closing door required in case of envelope style request dialogs */
14919     if (!skip_request)
14920       CloseDoor(DOOR_CLOSE_1);
14921
14922 #if defined(NETWORK_AVALIABLE)
14923     if (options.network)
14924       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14925     else
14926 #endif
14927     {
14928       if (quick_quit)
14929         FadeSkipNextFadeIn();
14930
14931       SetGameStatus(GAME_MODE_MAIN);
14932
14933       DrawMainMenu();
14934     }
14935   }
14936   else          /* continue playing the game */
14937   {
14938     if (tape.playing && tape.deactivate_display)
14939       TapeDeactivateDisplayOff(TRUE);
14940
14941     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14942
14943     if (tape.playing && tape.deactivate_display)
14944       TapeDeactivateDisplayOn();
14945   }
14946 }
14947
14948 void RequestQuitGame(boolean ask_if_really_quit)
14949 {
14950   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14951   boolean skip_request = AllPlayersGone || quick_quit;
14952
14953   RequestQuitGameExt(skip_request, quick_quit,
14954                      "Do you really want to quit the game?");
14955 }
14956
14957
14958 /* ------------------------------------------------------------------------- */
14959 /* random generator functions                                                */
14960 /* ------------------------------------------------------------------------- */
14961
14962 unsigned int InitEngineRandom_RND(int seed)
14963 {
14964   game.num_random_calls = 0;
14965
14966   return InitEngineRandom(seed);
14967 }
14968
14969 unsigned int RND(int max)
14970 {
14971   if (max > 0)
14972   {
14973     game.num_random_calls++;
14974
14975     return GetEngineRandom(max);
14976   }
14977
14978   return 0;
14979 }
14980
14981
14982 /* ------------------------------------------------------------------------- */
14983 /* game engine snapshot handling functions                                   */
14984 /* ------------------------------------------------------------------------- */
14985
14986 struct EngineSnapshotInfo
14987 {
14988   /* runtime values for custom element collect score */
14989   int collect_score[NUM_CUSTOM_ELEMENTS];
14990
14991   /* runtime values for group element choice position */
14992   int choice_pos[NUM_GROUP_ELEMENTS];
14993
14994   /* runtime values for belt position animations */
14995   int belt_graphic[4][NUM_BELT_PARTS];
14996   int belt_anim_mode[4][NUM_BELT_PARTS];
14997 };
14998
14999 static struct EngineSnapshotInfo engine_snapshot_rnd;
15000 static char *snapshot_level_identifier = NULL;
15001 static int snapshot_level_nr = -1;
15002
15003 static void SaveEngineSnapshotValues_RND()
15004 {
15005   static int belt_base_active_element[4] =
15006   {
15007     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15008     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15009     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15010     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15011   };
15012   int i, j;
15013
15014   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15015   {
15016     int element = EL_CUSTOM_START + i;
15017
15018     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15019   }
15020
15021   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15022   {
15023     int element = EL_GROUP_START + i;
15024
15025     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15026   }
15027
15028   for (i = 0; i < 4; i++)
15029   {
15030     for (j = 0; j < NUM_BELT_PARTS; j++)
15031     {
15032       int element = belt_base_active_element[i] + j;
15033       int graphic = el2img(element);
15034       int anim_mode = graphic_info[graphic].anim_mode;
15035
15036       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15037       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15038     }
15039   }
15040 }
15041
15042 static void LoadEngineSnapshotValues_RND()
15043 {
15044   unsigned int num_random_calls = game.num_random_calls;
15045   int i, j;
15046
15047   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15048   {
15049     int element = EL_CUSTOM_START + i;
15050
15051     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15052   }
15053
15054   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15055   {
15056     int element = EL_GROUP_START + i;
15057
15058     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15059   }
15060
15061   for (i = 0; i < 4; i++)
15062   {
15063     for (j = 0; j < NUM_BELT_PARTS; j++)
15064     {
15065       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15066       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15067
15068       graphic_info[graphic].anim_mode = anim_mode;
15069     }
15070   }
15071
15072   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15073   {
15074     InitRND(tape.random_seed);
15075     for (i = 0; i < num_random_calls; i++)
15076       RND(1);
15077   }
15078
15079   if (game.num_random_calls != num_random_calls)
15080   {
15081     Error(ERR_INFO, "number of random calls out of sync");
15082     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15083     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15084     Error(ERR_EXIT, "this should not happen -- please debug");
15085   }
15086 }
15087
15088 void FreeEngineSnapshotSingle()
15089 {
15090   FreeSnapshotSingle();
15091
15092   setString(&snapshot_level_identifier, NULL);
15093   snapshot_level_nr = -1;
15094 }
15095
15096 void FreeEngineSnapshotList()
15097 {
15098   FreeSnapshotList();
15099 }
15100
15101 ListNode *SaveEngineSnapshotBuffers()
15102 {
15103   ListNode *buffers = NULL;
15104
15105   /* copy some special values to a structure better suited for the snapshot */
15106
15107   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15108     SaveEngineSnapshotValues_RND();
15109   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15110     SaveEngineSnapshotValues_EM();
15111   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15112     SaveEngineSnapshotValues_SP(&buffers);
15113   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15114     SaveEngineSnapshotValues_MM(&buffers);
15115
15116   /* save values stored in special snapshot structure */
15117
15118   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15119     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15120   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15121     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15122   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15123     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15124   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15125     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15126
15127   /* save further RND engine values */
15128
15129   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15130   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15131   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15132
15133   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
15134   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
15135   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
15136   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
15137
15138   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15139   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15140   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15141   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15142   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15143
15144   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15145   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15146   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15147
15148   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15149
15150   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15151
15152   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15153   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15154
15155   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
15156   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15157   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15158   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15159   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15160   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15161   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15162   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15163   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15164   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15165   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15166   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15167   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15168   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15169   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15170   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15171   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15172   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15173
15174   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15175   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15176
15177   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15178   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15179   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15180
15181   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15182   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15183
15184   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15185   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15186   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15187   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15188   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15189
15190   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15191   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15192
15193 #if 0
15194   ListNode *node = engine_snapshot_list_rnd;
15195   int num_bytes = 0;
15196
15197   while (node != NULL)
15198   {
15199     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15200
15201     node = node->next;
15202   }
15203
15204   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15205 #endif
15206
15207   return buffers;
15208 }
15209
15210 void SaveEngineSnapshotSingle()
15211 {
15212   ListNode *buffers = SaveEngineSnapshotBuffers();
15213
15214   /* finally save all snapshot buffers to single snapshot */
15215   SaveSnapshotSingle(buffers);
15216
15217   /* save level identification information */
15218   setString(&snapshot_level_identifier, leveldir_current->identifier);
15219   snapshot_level_nr = level_nr;
15220 }
15221
15222 boolean CheckSaveEngineSnapshotToList()
15223 {
15224   boolean save_snapshot =
15225     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15226      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15227       game.snapshot.changed_action) ||
15228      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15229       game.snapshot.collected_item));
15230
15231   game.snapshot.changed_action = FALSE;
15232   game.snapshot.collected_item = FALSE;
15233   game.snapshot.save_snapshot = save_snapshot;
15234
15235   return save_snapshot;
15236 }
15237
15238 void SaveEngineSnapshotToList()
15239 {
15240   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15241       tape.quick_resume)
15242     return;
15243
15244   ListNode *buffers = SaveEngineSnapshotBuffers();
15245
15246   /* finally save all snapshot buffers to snapshot list */
15247   SaveSnapshotToList(buffers);
15248 }
15249
15250 void SaveEngineSnapshotToListInitial()
15251 {
15252   FreeEngineSnapshotList();
15253
15254   SaveEngineSnapshotToList();
15255 }
15256
15257 void LoadEngineSnapshotValues()
15258 {
15259   /* restore special values from snapshot structure */
15260
15261   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15262     LoadEngineSnapshotValues_RND();
15263   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15264     LoadEngineSnapshotValues_EM();
15265   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15266     LoadEngineSnapshotValues_SP();
15267   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15268     LoadEngineSnapshotValues_MM();
15269 }
15270
15271 void LoadEngineSnapshotSingle()
15272 {
15273   LoadSnapshotSingle();
15274
15275   LoadEngineSnapshotValues();
15276 }
15277
15278 void LoadEngineSnapshot_Undo(int steps)
15279 {
15280   LoadSnapshotFromList_Older(steps);
15281
15282   LoadEngineSnapshotValues();
15283 }
15284
15285 void LoadEngineSnapshot_Redo(int steps)
15286 {
15287   LoadSnapshotFromList_Newer(steps);
15288
15289   LoadEngineSnapshotValues();
15290 }
15291
15292 boolean CheckEngineSnapshotSingle()
15293 {
15294   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15295           snapshot_level_nr == level_nr);
15296 }
15297
15298 boolean CheckEngineSnapshotList()
15299 {
15300   return CheckSnapshotList();
15301 }
15302
15303
15304 /* ---------- new game button stuff ---------------------------------------- */
15305
15306 static struct
15307 {
15308   int graphic;
15309   struct XY *pos;
15310   int gadget_id;
15311   char *infotext;
15312 } gamebutton_info[NUM_GAME_BUTTONS] =
15313 {
15314   {
15315     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
15316     GAME_CTRL_ID_STOP,                  "stop game"
15317   },
15318   {
15319     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
15320     GAME_CTRL_ID_PAUSE,                 "pause game"
15321   },
15322   {
15323     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
15324     GAME_CTRL_ID_PLAY,                  "play game"
15325   },
15326   {
15327     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
15328     GAME_CTRL_ID_UNDO,                  "undo step"
15329   },
15330   {
15331     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
15332     GAME_CTRL_ID_REDO,                  "redo step"
15333   },
15334   {
15335     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
15336     GAME_CTRL_ID_SAVE,                  "save game"
15337   },
15338   {
15339     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
15340     GAME_CTRL_ID_PAUSE2,                "pause game"
15341   },
15342   {
15343     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
15344     GAME_CTRL_ID_LOAD,                  "load game"
15345   },
15346   {
15347     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
15348     SOUND_CTRL_ID_MUSIC,                "background music on/off"
15349   },
15350   {
15351     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
15352     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
15353   },
15354   {
15355     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
15356     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
15357   }
15358 };
15359
15360 void CreateGameButtons()
15361 {
15362   int i;
15363
15364   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15365   {
15366     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
15367     struct XY *pos = gamebutton_info[i].pos;
15368     struct GadgetInfo *gi;
15369     int button_type;
15370     boolean checked;
15371     unsigned int event_mask;
15372     int base_x = (tape.show_game_buttons ? VX : DX);
15373     int base_y = (tape.show_game_buttons ? VY : DY);
15374     int gd_x   = gfx->src_x;
15375     int gd_y   = gfx->src_y;
15376     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15377     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15378     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15379     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15380     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15381     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15382     int id = i;
15383
15384     if (gfx->bitmap == NULL)
15385     {
15386       game_gadget[id] = NULL;
15387
15388       continue;
15389     }
15390
15391     if (id == GAME_CTRL_ID_STOP ||
15392         id == GAME_CTRL_ID_PLAY ||
15393         id == GAME_CTRL_ID_SAVE ||
15394         id == GAME_CTRL_ID_LOAD)
15395     {
15396       button_type = GD_TYPE_NORMAL_BUTTON;
15397       checked = FALSE;
15398       event_mask = GD_EVENT_RELEASED;
15399     }
15400     else if (id == GAME_CTRL_ID_UNDO ||
15401              id == GAME_CTRL_ID_REDO)
15402     {
15403       button_type = GD_TYPE_NORMAL_BUTTON;
15404       checked = FALSE;
15405       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15406     }
15407     else
15408     {
15409       button_type = GD_TYPE_CHECK_BUTTON;
15410       checked =
15411         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15412          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15413          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15414       event_mask = GD_EVENT_PRESSED;
15415     }
15416
15417     gi = CreateGadget(GDI_CUSTOM_ID, id,
15418                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15419                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15420                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15421                       GDI_WIDTH, gfx->width,
15422                       GDI_HEIGHT, gfx->height,
15423                       GDI_TYPE, button_type,
15424                       GDI_STATE, GD_BUTTON_UNPRESSED,
15425                       GDI_CHECKED, checked,
15426                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15427                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15428                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15429                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15430                       GDI_DIRECT_DRAW, FALSE,
15431                       GDI_EVENT_MASK, event_mask,
15432                       GDI_CALLBACK_ACTION, HandleGameButtons,
15433                       GDI_END);
15434
15435     if (gi == NULL)
15436       Error(ERR_EXIT, "cannot create gadget");
15437
15438     game_gadget[id] = gi;
15439   }
15440 }
15441
15442 void FreeGameButtons()
15443 {
15444   int i;
15445
15446   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15447     FreeGadget(game_gadget[i]);
15448 }
15449
15450 static void UnmapGameButtonsAtSamePosition(int id)
15451 {
15452   int i;
15453
15454   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15455     if (i != id &&
15456         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15457         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15458       UnmapGadget(game_gadget[i]);
15459 }
15460
15461 static void UnmapGameButtonsAtSamePosition_All()
15462 {
15463   if (setup.show_snapshot_buttons)
15464   {
15465     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15466     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15467     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15468   }
15469   else
15470   {
15471     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15472     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15473     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15474   }
15475 }
15476
15477 static void MapGameButtonsAtSamePosition(int id)
15478 {
15479   int i;
15480
15481   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15482     if (i != id &&
15483         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15484         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15485       MapGadget(game_gadget[i]);
15486
15487   UnmapGameButtonsAtSamePosition_All();
15488 }
15489
15490 void MapUndoRedoButtons()
15491 {
15492   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15493   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15494
15495   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15496   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15497
15498   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15499 }
15500
15501 void UnmapUndoRedoButtons()
15502 {
15503   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15504   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15505
15506   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15507   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15508
15509   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15510 }
15511
15512 void MapGameButtons()
15513 {
15514   int i;
15515
15516   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15517     if (i != GAME_CTRL_ID_UNDO &&
15518         i != GAME_CTRL_ID_REDO)
15519       MapGadget(game_gadget[i]);
15520
15521   UnmapGameButtonsAtSamePosition_All();
15522
15523   RedrawGameButtons();
15524 }
15525
15526 void UnmapGameButtons()
15527 {
15528   int i;
15529
15530   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15531     UnmapGadget(game_gadget[i]);
15532 }
15533
15534 void RedrawGameButtons()
15535 {
15536   int i;
15537
15538   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15539     RedrawGadget(game_gadget[i]);
15540
15541   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15542   redraw_mask &= ~REDRAW_ALL;
15543 }
15544
15545 void GameUndoRedoExt()
15546 {
15547   ClearPlayerAction();
15548
15549   tape.pausing = TRUE;
15550
15551   RedrawPlayfield();
15552   UpdateAndDisplayGameControlValues();
15553
15554   DrawCompleteVideoDisplay();
15555   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15556   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15557   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15558
15559   BackToFront();
15560 }
15561
15562 void GameUndo(int steps)
15563 {
15564   if (!CheckEngineSnapshotList())
15565     return;
15566
15567   LoadEngineSnapshot_Undo(steps);
15568
15569   GameUndoRedoExt();
15570 }
15571
15572 void GameRedo(int steps)
15573 {
15574   if (!CheckEngineSnapshotList())
15575     return;
15576
15577   LoadEngineSnapshot_Redo(steps);
15578
15579   GameUndoRedoExt();
15580 }
15581
15582 static void HandleGameButtonsExt(int id, int button)
15583 {
15584   static boolean game_undo_executed = FALSE;
15585   int steps = BUTTON_STEPSIZE(button);
15586   boolean handle_game_buttons =
15587     (game_status == GAME_MODE_PLAYING ||
15588      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15589
15590   if (!handle_game_buttons)
15591     return;
15592
15593   switch (id)
15594   {
15595     case GAME_CTRL_ID_STOP:
15596       if (game_status == GAME_MODE_MAIN)
15597         break;
15598
15599       if (tape.playing)
15600         TapeStop();
15601       else
15602         RequestQuitGame(TRUE);
15603
15604       break;
15605
15606     case GAME_CTRL_ID_PAUSE:
15607     case GAME_CTRL_ID_PAUSE2:
15608       if (options.network && game_status == GAME_MODE_PLAYING)
15609       {
15610 #if defined(NETWORK_AVALIABLE)
15611         if (tape.pausing)
15612           SendToServer_ContinuePlaying();
15613         else
15614           SendToServer_PausePlaying();
15615 #endif
15616       }
15617       else
15618         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15619
15620       game_undo_executed = FALSE;
15621
15622       break;
15623
15624     case GAME_CTRL_ID_PLAY:
15625       if (game_status == GAME_MODE_MAIN)
15626       {
15627         StartGameActions(options.network, setup.autorecord, level.random_seed);
15628       }
15629       else if (tape.pausing)
15630       {
15631 #if defined(NETWORK_AVALIABLE)
15632         if (options.network)
15633           SendToServer_ContinuePlaying();
15634         else
15635 #endif
15636           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15637       }
15638       break;
15639
15640     case GAME_CTRL_ID_UNDO:
15641       // Important: When using "save snapshot when collecting an item" mode,
15642       // load last (current) snapshot for first "undo" after pressing "pause"
15643       // (else the last-but-one snapshot would be loaded, because the snapshot
15644       // pointer already points to the last snapshot when pressing "pause",
15645       // which is fine for "every step/move" mode, but not for "every collect")
15646       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15647           !game_undo_executed)
15648         steps--;
15649
15650       game_undo_executed = TRUE;
15651
15652       GameUndo(steps);
15653       break;
15654
15655     case GAME_CTRL_ID_REDO:
15656       GameRedo(steps);
15657       break;
15658
15659     case GAME_CTRL_ID_SAVE:
15660       TapeQuickSave();
15661       break;
15662
15663     case GAME_CTRL_ID_LOAD:
15664       TapeQuickLoad();
15665       break;
15666
15667     case SOUND_CTRL_ID_MUSIC:
15668       if (setup.sound_music)
15669       { 
15670         setup.sound_music = FALSE;
15671
15672         FadeMusic();
15673       }
15674       else if (audio.music_available)
15675       { 
15676         setup.sound = setup.sound_music = TRUE;
15677
15678         SetAudioMode(setup.sound);
15679
15680         PlayLevelMusic();
15681       }
15682       break;
15683
15684     case SOUND_CTRL_ID_LOOPS:
15685       if (setup.sound_loops)
15686         setup.sound_loops = FALSE;
15687       else if (audio.loops_available)
15688       {
15689         setup.sound = setup.sound_loops = TRUE;
15690
15691         SetAudioMode(setup.sound);
15692       }
15693       break;
15694
15695     case SOUND_CTRL_ID_SIMPLE:
15696       if (setup.sound_simple)
15697         setup.sound_simple = FALSE;
15698       else if (audio.sound_available)
15699       {
15700         setup.sound = setup.sound_simple = TRUE;
15701
15702         SetAudioMode(setup.sound);
15703       }
15704       break;
15705
15706     default:
15707       break;
15708   }
15709 }
15710
15711 static void HandleGameButtons(struct GadgetInfo *gi)
15712 {
15713   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15714 }
15715
15716 void HandleSoundButtonKeys(Key key)
15717 {
15718
15719   if (key == setup.shortcut.sound_simple)
15720     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15721   else if (key == setup.shortcut.sound_loops)
15722     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15723   else if (key == setup.shortcut.sound_music)
15724     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15725 }