rnd-20090623-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER   (USE_NEW_STUFF          * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE  (USE_NEW_STUFF          * 1)
63
64 #define USE_PLAYER_REANIMATION          (USE_NEW_STUFF          * 1)
65
66 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
67
68 #define USE_NEW_PLAYER_ASSIGNMENTS      (USE_NEW_STUFF          * 1)
69
70 #define USE_DELAYED_GFX_REDRAW          (USE_NEW_STUFF          * 0)
71
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)                               \
74         GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
76         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
78         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)                           \
80         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)                               \
83              DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbledSand(x, y)                   \
85              DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)         \
87              DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)                           \
89              DrawTwinkleOnField(x, y)
90 #endif
91
92
93 /* for DigField() */
94 #define DF_NO_PUSH              0
95 #define DF_DIG                  1
96 #define DF_SNAP                 2
97
98 /* for MovePlayer() */
99 #define MP_NO_ACTION            0
100 #define MP_MOVING               1
101 #define MP_ACTION               2
102 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
103
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT             0
106 #define SCROLL_GO_ON            1
107
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START          0
110 #define EX_TYPE_NONE            0
111 #define EX_TYPE_NORMAL          (1 << 0)
112 #define EX_TYPE_CENTER          (1 << 1)
113 #define EX_TYPE_BORDER          (1 << 2)
114 #define EX_TYPE_CROSS           (1 << 3)
115 #define EX_TYPE_DYNA            (1 << 4)
116 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
117
118 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
122
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
139 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
140
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1               (DX + XX_LEVEL1)
143 #define DX_LEVEL2               (DX + XX_LEVEL2)
144 #define DX_LEVEL                (DX + XX_LEVEL)
145 #define DY_LEVEL                (DY + YY_LEVEL)
146 #define DX_EMERALDS             (DX + XX_EMERALDS)
147 #define DY_EMERALDS             (DY + YY_EMERALDS)
148 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
150 #define DX_KEYS                 (DX + XX_KEYS)
151 #define DY_KEYS                 (DY + YY_KEYS)
152 #define DX_SCORE                (DX + XX_SCORE)
153 #define DY_SCORE                (DY + YY_SCORE)
154 #define DX_TIME1                (DX + XX_TIME1)
155 #define DX_TIME2                (DX + XX_TIME2)
156 #define DX_TIME                 (DX + XX_TIME)
157 #define DY_TIME                 (DY + YY_TIME)
158
159 #if 1
160 /* game panel display and control definitions */
161
162 #define GAME_PANEL_LEVEL_NUMBER                 0
163 #define GAME_PANEL_GEMS                         1
164 #define GAME_PANEL_INVENTORY_COUNT              2
165 #define GAME_PANEL_INVENTORY_FIRST_1            3
166 #define GAME_PANEL_INVENTORY_FIRST_2            4
167 #define GAME_PANEL_INVENTORY_FIRST_3            5
168 #define GAME_PANEL_INVENTORY_FIRST_4            6
169 #define GAME_PANEL_INVENTORY_FIRST_5            7
170 #define GAME_PANEL_INVENTORY_FIRST_6            8
171 #define GAME_PANEL_INVENTORY_FIRST_7            9
172 #define GAME_PANEL_INVENTORY_FIRST_8            10
173 #define GAME_PANEL_INVENTORY_LAST_1             11
174 #define GAME_PANEL_INVENTORY_LAST_2             12
175 #define GAME_PANEL_INVENTORY_LAST_3             13
176 #define GAME_PANEL_INVENTORY_LAST_4             14
177 #define GAME_PANEL_INVENTORY_LAST_5             15
178 #define GAME_PANEL_INVENTORY_LAST_6             16
179 #define GAME_PANEL_INVENTORY_LAST_7             17
180 #define GAME_PANEL_INVENTORY_LAST_8             18
181 #define GAME_PANEL_KEY_1                        19
182 #define GAME_PANEL_KEY_2                        20
183 #define GAME_PANEL_KEY_3                        21
184 #define GAME_PANEL_KEY_4                        22
185 #define GAME_PANEL_KEY_5                        23
186 #define GAME_PANEL_KEY_6                        24
187 #define GAME_PANEL_KEY_7                        25
188 #define GAME_PANEL_KEY_8                        26
189 #define GAME_PANEL_KEY_WHITE                    27
190 #define GAME_PANEL_KEY_WHITE_COUNT              28
191 #define GAME_PANEL_SCORE                        29
192 #define GAME_PANEL_HIGHSCORE                    30
193 #define GAME_PANEL_TIME                         31
194 #define GAME_PANEL_TIME_HH                      32
195 #define GAME_PANEL_TIME_MM                      33
196 #define GAME_PANEL_TIME_SS                      34
197 #define GAME_PANEL_SHIELD_NORMAL                35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME           36
199 #define GAME_PANEL_SHIELD_DEADLY                37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME           38
201 #define GAME_PANEL_EXIT                         39
202 #define GAME_PANEL_EMC_MAGIC_BALL               40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        41
204 #define GAME_PANEL_LIGHT_SWITCH                 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME            43
206 #define GAME_PANEL_TIMEGATE_SWITCH              44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         45
208 #define GAME_PANEL_SWITCHGATE_SWITCH            46
209 #define GAME_PANEL_EMC_LENSES                   47
210 #define GAME_PANEL_EMC_LENSES_TIME              48
211 #define GAME_PANEL_EMC_MAGNIFIER                49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME           50
213 #define GAME_PANEL_BALLOON_SWITCH               51
214 #define GAME_PANEL_DYNABOMB_NUMBER              52
215 #define GAME_PANEL_DYNABOMB_SIZE                53
216 #define GAME_PANEL_DYNABOMB_POWER               54
217 #define GAME_PANEL_PENGUINS                     55
218 #define GAME_PANEL_SOKOBAN_OBJECTS              56
219 #define GAME_PANEL_SOKOBAN_FIELDS               57
220 #define GAME_PANEL_ROBOT_WHEEL                  58
221 #define GAME_PANEL_CONVEYOR_BELT_1              59
222 #define GAME_PANEL_CONVEYOR_BELT_2              60
223 #define GAME_PANEL_CONVEYOR_BELT_3              61
224 #define GAME_PANEL_CONVEYOR_BELT_4              62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       66
229 #define GAME_PANEL_MAGIC_WALL                   67
230 #define GAME_PANEL_MAGIC_WALL_TIME              68
231 #define GAME_PANEL_GRAVITY_STATE                69
232 #define GAME_PANEL_GRAPHIC_1                    70
233 #define GAME_PANEL_GRAPHIC_2                    71
234 #define GAME_PANEL_GRAPHIC_3                    72
235 #define GAME_PANEL_GRAPHIC_4                    73
236 #define GAME_PANEL_GRAPHIC_5                    74
237 #define GAME_PANEL_GRAPHIC_6                    75
238 #define GAME_PANEL_GRAPHIC_7                    76
239 #define GAME_PANEL_GRAPHIC_8                    77
240 #define GAME_PANEL_ELEMENT_1                    78
241 #define GAME_PANEL_ELEMENT_2                    79
242 #define GAME_PANEL_ELEMENT_3                    80
243 #define GAME_PANEL_ELEMENT_4                    81
244 #define GAME_PANEL_ELEMENT_5                    82
245 #define GAME_PANEL_ELEMENT_6                    83
246 #define GAME_PANEL_ELEMENT_7                    84
247 #define GAME_PANEL_ELEMENT_8                    85
248 #define GAME_PANEL_ELEMENT_COUNT_1              86
249 #define GAME_PANEL_ELEMENT_COUNT_2              87
250 #define GAME_PANEL_ELEMENT_COUNT_3              88
251 #define GAME_PANEL_ELEMENT_COUNT_4              89
252 #define GAME_PANEL_ELEMENT_COUNT_5              90
253 #define GAME_PANEL_ELEMENT_COUNT_6              91
254 #define GAME_PANEL_ELEMENT_COUNT_7              92
255 #define GAME_PANEL_ELEMENT_COUNT_8              93
256 #define GAME_PANEL_CE_SCORE_1                   94
257 #define GAME_PANEL_CE_SCORE_2                   95
258 #define GAME_PANEL_CE_SCORE_3                   96
259 #define GAME_PANEL_CE_SCORE_4                   97
260 #define GAME_PANEL_CE_SCORE_5                   98
261 #define GAME_PANEL_CE_SCORE_6                   99
262 #define GAME_PANEL_CE_SCORE_7                   100
263 #define GAME_PANEL_CE_SCORE_8                   101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT           102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT           103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT           104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT           105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT           106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT           107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT           108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT           109
272 #define GAME_PANEL_PLAYER_NAME                  110
273 #define GAME_PANEL_LEVEL_NAME                   111
274 #define GAME_PANEL_LEVEL_AUTHOR                 112
275
276 #define NUM_GAME_PANEL_CONTROLS                 113
277
278 struct GamePanelOrderInfo
279 {
280   int nr;
281   int sort_priority;
282 };
283
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
285
286 struct GamePanelControlInfo
287 {
288   int nr;
289
290   struct TextPosInfo *pos;
291   int type;
292
293   int value, last_value;
294   int frame, last_frame;
295   int gfx_frame;
296   int gfx_random;
297 };
298
299 static struct GamePanelControlInfo game_panel_controls[] =
300 {
301   {
302     GAME_PANEL_LEVEL_NUMBER,
303     &game.panel.level_number,
304     TYPE_INTEGER,
305   },
306   {
307     GAME_PANEL_GEMS,
308     &game.panel.gems,
309     TYPE_INTEGER,
310   },
311   {
312     GAME_PANEL_INVENTORY_COUNT,
313     &game.panel.inventory_count,
314     TYPE_INTEGER,
315   },
316   {
317     GAME_PANEL_INVENTORY_FIRST_1,
318     &game.panel.inventory_first[0],
319     TYPE_ELEMENT,
320   },
321   {
322     GAME_PANEL_INVENTORY_FIRST_2,
323     &game.panel.inventory_first[1],
324     TYPE_ELEMENT,
325   },
326   {
327     GAME_PANEL_INVENTORY_FIRST_3,
328     &game.panel.inventory_first[2],
329     TYPE_ELEMENT,
330   },
331   {
332     GAME_PANEL_INVENTORY_FIRST_4,
333     &game.panel.inventory_first[3],
334     TYPE_ELEMENT,
335   },
336   {
337     GAME_PANEL_INVENTORY_FIRST_5,
338     &game.panel.inventory_first[4],
339     TYPE_ELEMENT,
340   },
341   {
342     GAME_PANEL_INVENTORY_FIRST_6,
343     &game.panel.inventory_first[5],
344     TYPE_ELEMENT,
345   },
346   {
347     GAME_PANEL_INVENTORY_FIRST_7,
348     &game.panel.inventory_first[6],
349     TYPE_ELEMENT,
350   },
351   {
352     GAME_PANEL_INVENTORY_FIRST_8,
353     &game.panel.inventory_first[7],
354     TYPE_ELEMENT,
355   },
356   {
357     GAME_PANEL_INVENTORY_LAST_1,
358     &game.panel.inventory_last[0],
359     TYPE_ELEMENT,
360   },
361   {
362     GAME_PANEL_INVENTORY_LAST_2,
363     &game.panel.inventory_last[1],
364     TYPE_ELEMENT,
365   },
366   {
367     GAME_PANEL_INVENTORY_LAST_3,
368     &game.panel.inventory_last[2],
369     TYPE_ELEMENT,
370   },
371   {
372     GAME_PANEL_INVENTORY_LAST_4,
373     &game.panel.inventory_last[3],
374     TYPE_ELEMENT,
375   },
376   {
377     GAME_PANEL_INVENTORY_LAST_5,
378     &game.panel.inventory_last[4],
379     TYPE_ELEMENT,
380   },
381   {
382     GAME_PANEL_INVENTORY_LAST_6,
383     &game.panel.inventory_last[5],
384     TYPE_ELEMENT,
385   },
386   {
387     GAME_PANEL_INVENTORY_LAST_7,
388     &game.panel.inventory_last[6],
389     TYPE_ELEMENT,
390   },
391   {
392     GAME_PANEL_INVENTORY_LAST_8,
393     &game.panel.inventory_last[7],
394     TYPE_ELEMENT,
395   },
396   {
397     GAME_PANEL_KEY_1,
398     &game.panel.key[0],
399     TYPE_ELEMENT,
400   },
401   {
402     GAME_PANEL_KEY_2,
403     &game.panel.key[1],
404     TYPE_ELEMENT,
405   },
406   {
407     GAME_PANEL_KEY_3,
408     &game.panel.key[2],
409     TYPE_ELEMENT,
410   },
411   {
412     GAME_PANEL_KEY_4,
413     &game.panel.key[3],
414     TYPE_ELEMENT,
415   },
416   {
417     GAME_PANEL_KEY_5,
418     &game.panel.key[4],
419     TYPE_ELEMENT,
420   },
421   {
422     GAME_PANEL_KEY_6,
423     &game.panel.key[5],
424     TYPE_ELEMENT,
425   },
426   {
427     GAME_PANEL_KEY_7,
428     &game.panel.key[6],
429     TYPE_ELEMENT,
430   },
431   {
432     GAME_PANEL_KEY_8,
433     &game.panel.key[7],
434     TYPE_ELEMENT,
435   },
436   {
437     GAME_PANEL_KEY_WHITE,
438     &game.panel.key_white,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_KEY_WHITE_COUNT,
443     &game.panel.key_white_count,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SCORE,
448     &game.panel.score,
449     TYPE_INTEGER,
450   },
451   {
452     GAME_PANEL_HIGHSCORE,
453     &game.panel.highscore,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_TIME,
458     &game.panel.time,
459     TYPE_INTEGER,
460   },
461   {
462     GAME_PANEL_TIME_HH,
463     &game.panel.time_hh,
464     TYPE_INTEGER,
465   },
466   {
467     GAME_PANEL_TIME_MM,
468     &game.panel.time_mm,
469     TYPE_INTEGER,
470   },
471   {
472     GAME_PANEL_TIME_SS,
473     &game.panel.time_ss,
474     TYPE_INTEGER,
475   },
476   {
477     GAME_PANEL_SHIELD_NORMAL,
478     &game.panel.shield_normal,
479     TYPE_ELEMENT,
480   },
481   {
482     GAME_PANEL_SHIELD_NORMAL_TIME,
483     &game.panel.shield_normal_time,
484     TYPE_INTEGER,
485   },
486   {
487     GAME_PANEL_SHIELD_DEADLY,
488     &game.panel.shield_deadly,
489     TYPE_ELEMENT,
490   },
491   {
492     GAME_PANEL_SHIELD_DEADLY_TIME,
493     &game.panel.shield_deadly_time,
494     TYPE_INTEGER,
495   },
496   {
497     GAME_PANEL_EXIT,
498     &game.panel.exit,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_MAGIC_BALL,
503     &game.panel.emc_magic_ball,
504     TYPE_ELEMENT,
505   },
506   {
507     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508     &game.panel.emc_magic_ball_switch,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_LIGHT_SWITCH,
513     &game.panel.light_switch,
514     TYPE_ELEMENT,
515   },
516   {
517     GAME_PANEL_LIGHT_SWITCH_TIME,
518     &game.panel.light_switch_time,
519     TYPE_INTEGER,
520   },
521   {
522     GAME_PANEL_TIMEGATE_SWITCH,
523     &game.panel.timegate_switch,
524     TYPE_ELEMENT,
525   },
526   {
527     GAME_PANEL_TIMEGATE_SWITCH_TIME,
528     &game.panel.timegate_switch_time,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_SWITCHGATE_SWITCH,
533     &game.panel.switchgate_switch,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_EMC_LENSES,
538     &game.panel.emc_lenses,
539     TYPE_ELEMENT,
540   },
541   {
542     GAME_PANEL_EMC_LENSES_TIME,
543     &game.panel.emc_lenses_time,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_EMC_MAGNIFIER,
548     &game.panel.emc_magnifier,
549     TYPE_ELEMENT,
550   },
551   {
552     GAME_PANEL_EMC_MAGNIFIER_TIME,
553     &game.panel.emc_magnifier_time,
554     TYPE_INTEGER,
555   },
556   {
557     GAME_PANEL_BALLOON_SWITCH,
558     &game.panel.balloon_switch,
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_DYNABOMB_NUMBER,
563     &game.panel.dynabomb_number,
564     TYPE_INTEGER,
565   },
566   {
567     GAME_PANEL_DYNABOMB_SIZE,
568     &game.panel.dynabomb_size,
569     TYPE_INTEGER,
570   },
571   {
572     GAME_PANEL_DYNABOMB_POWER,
573     &game.panel.dynabomb_power,
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_PENGUINS,
578     &game.panel.penguins,
579     TYPE_INTEGER,
580   },
581   {
582     GAME_PANEL_SOKOBAN_OBJECTS,
583     &game.panel.sokoban_objects,
584     TYPE_INTEGER,
585   },
586   {
587     GAME_PANEL_SOKOBAN_FIELDS,
588     &game.panel.sokoban_fields,
589     TYPE_INTEGER,
590   },
591   {
592     GAME_PANEL_ROBOT_WHEEL,
593     &game.panel.robot_wheel,
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_CONVEYOR_BELT_1,
598     &game.panel.conveyor_belt[0],
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_CONVEYOR_BELT_2,
603     &game.panel.conveyor_belt[1],
604     TYPE_ELEMENT,
605   },
606   {
607     GAME_PANEL_CONVEYOR_BELT_3,
608     &game.panel.conveyor_belt[2],
609     TYPE_ELEMENT,
610   },
611   {
612     GAME_PANEL_CONVEYOR_BELT_4,
613     &game.panel.conveyor_belt[3],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618     &game.panel.conveyor_belt_switch[0],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623     &game.panel.conveyor_belt_switch[1],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628     &game.panel.conveyor_belt_switch[2],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633     &game.panel.conveyor_belt_switch[3],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_MAGIC_WALL,
638     &game.panel.magic_wall,
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_MAGIC_WALL_TIME,
643     &game.panel.magic_wall_time,
644     TYPE_INTEGER,
645   },
646   {
647     GAME_PANEL_GRAVITY_STATE,
648     &game.panel.gravity_state,
649     TYPE_STRING,
650   },
651   {
652     GAME_PANEL_GRAPHIC_1,
653     &game.panel.graphic[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_GRAPHIC_2,
658     &game.panel.graphic[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_GRAPHIC_3,
663     &game.panel.graphic[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_GRAPHIC_4,
668     &game.panel.graphic[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_GRAPHIC_5,
673     &game.panel.graphic[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_GRAPHIC_6,
678     &game.panel.graphic[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_GRAPHIC_7,
683     &game.panel.graphic[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_GRAPHIC_8,
688     &game.panel.graphic[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_1,
693     &game.panel.element[0],
694     TYPE_ELEMENT,
695   },
696   {
697     GAME_PANEL_ELEMENT_2,
698     &game.panel.element[1],
699     TYPE_ELEMENT,
700   },
701   {
702     GAME_PANEL_ELEMENT_3,
703     &game.panel.element[2],
704     TYPE_ELEMENT,
705   },
706   {
707     GAME_PANEL_ELEMENT_4,
708     &game.panel.element[3],
709     TYPE_ELEMENT,
710   },
711   {
712     GAME_PANEL_ELEMENT_5,
713     &game.panel.element[4],
714     TYPE_ELEMENT,
715   },
716   {
717     GAME_PANEL_ELEMENT_6,
718     &game.panel.element[5],
719     TYPE_ELEMENT,
720   },
721   {
722     GAME_PANEL_ELEMENT_7,
723     &game.panel.element[6],
724     TYPE_ELEMENT,
725   },
726   {
727     GAME_PANEL_ELEMENT_8,
728     &game.panel.element[7],
729     TYPE_ELEMENT,
730   },
731   {
732     GAME_PANEL_ELEMENT_COUNT_1,
733     &game.panel.element_count[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_ELEMENT_COUNT_2,
738     &game.panel.element_count[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_ELEMENT_COUNT_3,
743     &game.panel.element_count[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_ELEMENT_COUNT_4,
748     &game.panel.element_count[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_ELEMENT_COUNT_5,
753     &game.panel.element_count[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_ELEMENT_COUNT_6,
758     &game.panel.element_count[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_ELEMENT_COUNT_7,
763     &game.panel.element_count[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_ELEMENT_COUNT_8,
768     &game.panel.element_count[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1,
773     &game.panel.ce_score[0],
774     TYPE_INTEGER,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2,
778     &game.panel.ce_score[1],
779     TYPE_INTEGER,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3,
783     &game.panel.ce_score[2],
784     TYPE_INTEGER,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4,
788     &game.panel.ce_score[3],
789     TYPE_INTEGER,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5,
793     &game.panel.ce_score[4],
794     TYPE_INTEGER,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6,
798     &game.panel.ce_score[5],
799     TYPE_INTEGER,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7,
803     &game.panel.ce_score[6],
804     TYPE_INTEGER,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8,
808     &game.panel.ce_score[7],
809     TYPE_INTEGER,
810   },
811   {
812     GAME_PANEL_CE_SCORE_1_ELEMENT,
813     &game.panel.ce_score_element[0],
814     TYPE_ELEMENT,
815   },
816   {
817     GAME_PANEL_CE_SCORE_2_ELEMENT,
818     &game.panel.ce_score_element[1],
819     TYPE_ELEMENT,
820   },
821   {
822     GAME_PANEL_CE_SCORE_3_ELEMENT,
823     &game.panel.ce_score_element[2],
824     TYPE_ELEMENT,
825   },
826   {
827     GAME_PANEL_CE_SCORE_4_ELEMENT,
828     &game.panel.ce_score_element[3],
829     TYPE_ELEMENT,
830   },
831   {
832     GAME_PANEL_CE_SCORE_5_ELEMENT,
833     &game.panel.ce_score_element[4],
834     TYPE_ELEMENT,
835   },
836   {
837     GAME_PANEL_CE_SCORE_6_ELEMENT,
838     &game.panel.ce_score_element[5],
839     TYPE_ELEMENT,
840   },
841   {
842     GAME_PANEL_CE_SCORE_7_ELEMENT,
843     &game.panel.ce_score_element[6],
844     TYPE_ELEMENT,
845   },
846   {
847     GAME_PANEL_CE_SCORE_8_ELEMENT,
848     &game.panel.ce_score_element[7],
849     TYPE_ELEMENT,
850   },
851   {
852     GAME_PANEL_PLAYER_NAME,
853     &game.panel.player_name,
854     TYPE_STRING,
855   },
856   {
857     GAME_PANEL_LEVEL_NAME,
858     &game.panel.level_name,
859     TYPE_STRING,
860   },
861   {
862     GAME_PANEL_LEVEL_AUTHOR,
863     &game.panel.level_author,
864     TYPE_STRING,
865   },
866
867   {
868     -1,
869     NULL,
870     -1,
871   }
872 };
873 #endif
874
875
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING      3
878 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION   2
880 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
881
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF  -1
884 #define INITIAL_MOVE_DELAY_ON   0
885
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED    32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED   4
890 #define MOVE_DELAY_MAX_SPEED    1
891
892 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
894
895 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
897
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN       (1)
901 #define MOVE_STEPSIZE_MAX       (TILEX)
902
903 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
905
906 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
907
908 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
909                                  RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
911                                  RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
913                                  RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
915                                     (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
917                                  RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
920                                  RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
922                                  RND((c)->delay_random))
923
924
925 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
926          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
927
928 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
929         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
930          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
931          (be) + (e) - EL_SELF)
932
933 #define GET_PLAYER_FROM_BITS(p)                                         \
934         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
935
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
937         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
938          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
939          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
940          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
941          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
942          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
943          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
944          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
945          (e))
946
947 #define CAN_GROW_INTO(e)                                                \
948         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
949
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
951                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (CAN_MOVE_INTO_ACID(e) &&       \
957                                          Feld[x][y] == EL_ACID) ||      \
958                                         (condition)))
959
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
961                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
962                                         (CAN_MOVE_INTO_ACID(e) &&       \
963                                          Feld[x][y] == EL_ACID) ||      \
964                                         (condition)))
965
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
967                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
968                                         (condition) ||                  \
969                                         (CAN_MOVE_INTO_ACID(e) &&       \
970                                          Feld[x][y] == EL_ACID) ||      \
971                                         (DONT_COLLIDE_WITH(e) &&        \
972                                          IS_PLAYER(x, y) &&             \
973                                          !PLAYER_ENEMY_PROTECTED(x, y))))
974
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
976         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
977
978 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
979         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
980
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
982         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
983
984 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
985         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
987
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
990
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
992         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
993
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
995         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
996
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
999
1000 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1002
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
1006                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008                                                  IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
1010         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1011
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1013         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1014
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1016         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1019         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
1020                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1021
1022 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1023
1024 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1025                 (!IS_PLAYER(x, y) &&                                    \
1026                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1027
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1029         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1030
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1033
1034 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1038
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP               0
1041 #define GAME_CTRL_ID_PAUSE              1
1042 #define GAME_CTRL_ID_PLAY               2
1043 #define SOUND_CTRL_ID_MUSIC             3
1044 #define SOUND_CTRL_ID_LOOPS             4
1045 #define SOUND_CTRL_ID_SIMPLE            5
1046
1047 #define NUM_GAME_BUTTONS                6
1048
1049
1050 /* forward declaration for internal use */
1051
1052 static void CreateField(int, int, int);
1053
1054 static void ResetGfxAnimation(int, int);
1055
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1058
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1063
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1068
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1075
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1079 #if 0
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1081 #endif
1082
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1086
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1089         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1091         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1093         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1096
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev)                             \
1099         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1101         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1103         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1104
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1113
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1116
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1124
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1135
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1139
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1142
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1144
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1148
1149 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1150 {                                                                       \
1151   if (recursion_loop_detected)                                          \
1152     return (rc);                                                        \
1153                                                                         \
1154   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1155   {                                                                     \
1156     recursion_loop_detected = TRUE;                                     \
1157     recursion_loop_element = (e);                                       \
1158   }                                                                     \
1159                                                                         \
1160   recursion_loop_depth++;                                               \
1161 }
1162
1163 #define RECURSION_LOOP_DETECTION_END()                                  \
1164 {                                                                       \
1165   recursion_loop_depth--;                                               \
1166 }
1167
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1171
1172 static int map_player_action[MAX_PLAYERS];
1173
1174
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after  */
1177 /* a specified time, eventually calling a function when changing             */
1178 /* ------------------------------------------------------------------------- */
1179
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1183
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1187
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1191
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1194
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1197
1198 struct ChangingElementInfo
1199 {
1200   int element;
1201   int target_element;
1202   int change_delay;
1203   void (*pre_change_function)(int x, int y);
1204   void (*change_function)(int x, int y);
1205   void (*post_change_function)(int x, int y);
1206 };
1207
1208 static struct ChangingElementInfo change_delay_list[] =
1209 {
1210   {
1211     EL_NUT_BREAKING,
1212     EL_EMERALD,
1213     6,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_PEARL_BREAKING,
1220     EL_EMPTY,
1221     8,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_OPENING,
1228     EL_EXIT_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_EXIT_CLOSING,
1236     EL_EXIT_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_OPENING,
1244     EL_STEEL_EXIT_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_STEEL_EXIT_CLOSING,
1252     EL_STEEL_EXIT_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_OPENING,
1260     EL_EM_EXIT_OPEN,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_EXIT_CLOSING,
1268 #if 1
1269     EL_EMPTY,
1270 #else
1271     EL_EM_EXIT_CLOSED,
1272 #endif
1273     29,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_EM_STEEL_EXIT_OPENING,
1280     EL_EM_STEEL_EXIT_OPEN,
1281     29,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_EM_STEEL_EXIT_CLOSING,
1288 #if 1
1289     EL_STEELWALL,
1290 #else
1291     EL_EM_STEEL_EXIT_CLOSED,
1292 #endif
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SP_EXIT_OPENING,
1300     EL_SP_EXIT_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SP_EXIT_CLOSING,
1308     EL_SP_EXIT_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_SWITCHGATE_OPENING,
1316     EL_SWITCHGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_SWITCHGATE_CLOSING,
1324     EL_SWITCHGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330   {
1331     EL_TIMEGATE_OPENING,
1332     EL_TIMEGATE_OPEN,
1333     29,
1334     NULL,
1335     NULL,
1336     NULL
1337   },
1338   {
1339     EL_TIMEGATE_CLOSING,
1340     EL_TIMEGATE_CLOSED,
1341     29,
1342     NULL,
1343     NULL,
1344     NULL
1345   },
1346
1347   {
1348     EL_ACID_SPLASH_LEFT,
1349     EL_EMPTY,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_ACID_SPLASH_RIGHT,
1357     EL_EMPTY,
1358     8,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE,
1365     EL_SP_BUGGY_BASE_ACTIVATING,
1366     0,
1367     InitBuggyBase,
1368     NULL,
1369     NULL
1370   },
1371   {
1372     EL_SP_BUGGY_BASE_ACTIVATING,
1373     EL_SP_BUGGY_BASE_ACTIVE,
1374     0,
1375     InitBuggyBase,
1376     NULL,
1377     NULL
1378   },
1379   {
1380     EL_SP_BUGGY_BASE_ACTIVE,
1381     EL_SP_BUGGY_BASE,
1382     0,
1383     InitBuggyBase,
1384     WarnBuggyBase,
1385     NULL
1386   },
1387   {
1388     EL_TRAP,
1389     EL_TRAP_ACTIVE,
1390     0,
1391     InitTrap,
1392     NULL,
1393     ActivateTrap
1394   },
1395   {
1396     EL_TRAP_ACTIVE,
1397     EL_TRAP,
1398     31,
1399     NULL,
1400     ChangeActiveTrap,
1401     NULL
1402   },
1403   {
1404     EL_ROBOT_WHEEL_ACTIVE,
1405     EL_ROBOT_WHEEL,
1406     0,
1407     InitRobotWheel,
1408     RunRobotWheel,
1409     StopRobotWheel
1410   },
1411   {
1412     EL_TIMEGATE_SWITCH_ACTIVE,
1413     EL_TIMEGATE_SWITCH,
1414     0,
1415     InitTimegateWheel,
1416     RunTimegateWheel,
1417     NULL
1418   },
1419   {
1420     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421     EL_DC_TIMEGATE_SWITCH,
1422     0,
1423     InitTimegateWheel,
1424     RunTimegateWheel,
1425     NULL
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL_ACTIVE,
1429     EL_EMC_MAGIC_BALL_ACTIVE,
1430     0,
1431     InitMagicBallDelay,
1432     NULL,
1433     ActivateMagicBall
1434   },
1435   {
1436     EL_EMC_SPRING_BUMPER_ACTIVE,
1437     EL_EMC_SPRING_BUMPER,
1438     8,
1439     NULL,
1440     NULL,
1441     NULL
1442   },
1443   {
1444     EL_DIAGONAL_SHRINKING,
1445     EL_UNDEFINED,
1446     0,
1447     NULL,
1448     NULL,
1449     NULL
1450   },
1451   {
1452     EL_DIAGONAL_GROWING,
1453     EL_UNDEFINED,
1454     0,
1455     NULL,
1456     NULL,
1457     NULL,
1458   },
1459
1460   {
1461     EL_UNDEFINED,
1462     EL_UNDEFINED,
1463     -1,
1464     NULL,
1465     NULL,
1466     NULL
1467   }
1468 };
1469
1470 struct
1471 {
1472   int element;
1473   int push_delay_fixed, push_delay_random;
1474 }
1475 push_delay_list[] =
1476 {
1477   { EL_SPRING,                  0, 0 },
1478   { EL_BALLOON,                 0, 0 },
1479
1480   { EL_SOKOBAN_OBJECT,          2, 0 },
1481   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1482   { EL_SATELLITE,               2, 0 },
1483   { EL_SP_DISK_YELLOW,          2, 0 },
1484
1485   { EL_UNDEFINED,               0, 0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int move_stepsize;
1492 }
1493 move_stepsize_list[] =
1494 {
1495   { EL_AMOEBA_DROP,             2 },
1496   { EL_AMOEBA_DROPPING,         2 },
1497   { EL_QUICKSAND_FILLING,       1 },
1498   { EL_QUICKSAND_EMPTYING,      1 },
1499   { EL_QUICKSAND_FAST_FILLING,  2 },
1500   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501   { EL_MAGIC_WALL_FILLING,      2 },
1502   { EL_MAGIC_WALL_EMPTYING,     2 },
1503   { EL_BD_MAGIC_WALL_FILLING,   2 },
1504   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1505   { EL_DC_MAGIC_WALL_FILLING,   2 },
1506   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1507
1508   { EL_UNDEFINED,               0 },
1509 };
1510
1511 struct
1512 {
1513   int element;
1514   int count;
1515 }
1516 collect_count_list[] =
1517 {
1518   { EL_EMERALD,                 1 },
1519   { EL_BD_DIAMOND,              1 },
1520   { EL_EMERALD_YELLOW,          1 },
1521   { EL_EMERALD_RED,             1 },
1522   { EL_EMERALD_PURPLE,          1 },
1523   { EL_DIAMOND,                 3 },
1524   { EL_SP_INFOTRON,             1 },
1525   { EL_PEARL,                   5 },
1526   { EL_CRYSTAL,                 8 },
1527
1528   { EL_UNDEFINED,               0 },
1529 };
1530
1531 struct
1532 {
1533   int element;
1534   int direction;
1535 }
1536 access_direction_list[] =
1537 {
1538   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1540   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1541   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1542   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1543   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1544   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1545   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1546   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1547   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1548   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1549
1550   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1551   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1552   { EL_SP_PORT_UP,                                                   MV_DOWN },
1553   { EL_SP_PORT_DOWN,                                         MV_UP           },
1554   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1555   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1556   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1558   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1559   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1560   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1561   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1562   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1563   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1564   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1565   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1566   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1567   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1568   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1569
1570   { EL_UNDEFINED,                       MV_NONE                              }
1571 };
1572
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1574
1575 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1578                                  IS_JUST_CHANGING(x, y))
1579
1580 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1581
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1587
1588 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1589                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1590                                      (y) += playfield_scan_delta_y)     \
1591                                 for ((x) = playfield_scan_start_x;      \
1592                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1593                                      (x) += playfield_scan_delta_x)
1594
1595 #ifdef DEBUG
1596 void DEBUG_SetMaximumDynamite()
1597 {
1598   int i;
1599
1600   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602       local_player->inventory_element[local_player->inventory_size++] =
1603         EL_DYNAMITE;
1604 }
1605 #endif
1606
1607 static void InitPlayfieldScanModeVars()
1608 {
1609   if (game.use_reverse_scan_direction)
1610   {
1611     playfield_scan_start_x = lev_fieldx - 1;
1612     playfield_scan_start_y = lev_fieldy - 1;
1613
1614     playfield_scan_delta_x = -1;
1615     playfield_scan_delta_y = -1;
1616   }
1617   else
1618   {
1619     playfield_scan_start_x = 0;
1620     playfield_scan_start_y = 0;
1621
1622     playfield_scan_delta_x = 1;
1623     playfield_scan_delta_y = 1;
1624   }
1625 }
1626
1627 static void InitPlayfieldScanMode(int mode)
1628 {
1629   game.use_reverse_scan_direction =
1630     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1631
1632   InitPlayfieldScanModeVars();
1633 }
1634
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1636 {
1637   move_stepsize =
1638     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1639
1640   /* make sure that stepsize value is always a power of 2 */
1641   move_stepsize = (1 << log_2(move_stepsize));
1642
1643   return TILEX / move_stepsize;
1644 }
1645
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647                                boolean init_game)
1648 {
1649   int player_nr = player->index_nr;
1650   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1652
1653   /* do no immediately change move delay -- the player might just be moving */
1654   player->move_delay_value_next = move_delay;
1655
1656   /* information if player can move must be set separately */
1657   player->cannot_move = cannot_move;
1658
1659   if (init_game)
1660   {
1661     player->move_delay       = game.initial_move_delay[player_nr];
1662     player->move_delay_value = game.initial_move_delay_value[player_nr];
1663
1664     player->move_delay_value_next = -1;
1665
1666     player->move_delay_reset_counter = 0;
1667   }
1668 }
1669
1670 void GetPlayerConfig()
1671 {
1672   GameFrameDelay = setup.game_frame_delay;
1673
1674   if (!audio.sound_available)
1675     setup.sound_simple = FALSE;
1676
1677   if (!audio.loops_available)
1678     setup.sound_loops = FALSE;
1679
1680   if (!audio.music_available)
1681     setup.sound_music = FALSE;
1682
1683   if (!video.fullscreen_available)
1684     setup.fullscreen = FALSE;
1685
1686   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1687
1688   SetAudioMode(setup.sound);
1689   InitJoysticks();
1690 }
1691
1692 int GetElementFromGroupElement(int element)
1693 {
1694   if (IS_GROUP_ELEMENT(element))
1695   {
1696     struct ElementGroupInfo *group = element_info[element].group;
1697     int last_anim_random_frame = gfx.anim_random_frame;
1698     int element_pos;
1699
1700     if (group->choice_mode == ANIM_RANDOM)
1701       gfx.anim_random_frame = RND(group->num_elements_resolved);
1702
1703     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704                                     group->choice_mode, 0,
1705                                     group->choice_pos);
1706
1707     if (group->choice_mode == ANIM_RANDOM)
1708       gfx.anim_random_frame = last_anim_random_frame;
1709
1710     group->choice_pos++;
1711
1712     element = group->element_resolved[element_pos];
1713   }
1714
1715   return element;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Feld[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Feld[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     /* ---------- initialize player's last field block delay --------------- */
1755
1756     /* always start with reliable default value (no adjustment needed) */
1757     player->block_delay_adjustment = 0;
1758
1759     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     /* special case 2: in game engines before 3.1.1, blocking was different */
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!options.network || player->connected)
1768     {
1769       player->active = TRUE;
1770
1771       /* remove potentially duplicate players */
1772       if (StorePlayer[jx][jy] == Feld[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Feld[x][y];
1776
1777       if (options.debug)
1778       {
1779         printf("Player %d activated.\n", player->element_nr);
1780         printf("[Local player is %d and currently %s.]\n",
1781                local_player->element_nr,
1782                local_player->active ? "active" : "not active");
1783       }
1784     }
1785
1786     Feld[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792 #if USE_PLAYER_REANIMATION
1793   if (!init_game)
1794   {
1795     int player_nr = GET_PLAYER_NR(element);
1796     struct PlayerInfo *player = &stored_player[player_nr];
1797
1798     if (player->active && player->killed)
1799       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1800   }
1801 #endif
1802 }
1803
1804 static void InitField(int x, int y, boolean init_game)
1805 {
1806   int element = Feld[x][y];
1807
1808   switch (element)
1809   {
1810     case EL_SP_MURPHY:
1811     case EL_PLAYER_1:
1812     case EL_PLAYER_2:
1813     case EL_PLAYER_3:
1814     case EL_PLAYER_4:
1815       InitPlayerField(x, y, element, init_game);
1816       break;
1817
1818     case EL_SOKOBAN_FIELD_PLAYER:
1819       element = Feld[x][y] = EL_PLAYER_1;
1820       InitField(x, y, init_game);
1821
1822       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823       InitField(x, y, init_game);
1824       break;
1825
1826     case EL_SOKOBAN_FIELD_EMPTY:
1827       local_player->sokobanfields_still_needed++;
1828       break;
1829
1830     case EL_STONEBLOCK:
1831       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1841       break;
1842
1843     case EL_BUG:
1844     case EL_BUG_RIGHT:
1845     case EL_BUG_UP:
1846     case EL_BUG_LEFT:
1847     case EL_BUG_DOWN:
1848     case EL_SPACESHIP:
1849     case EL_SPACESHIP_RIGHT:
1850     case EL_SPACESHIP_UP:
1851     case EL_SPACESHIP_LEFT:
1852     case EL_SPACESHIP_DOWN:
1853     case EL_BD_BUTTERFLY:
1854     case EL_BD_BUTTERFLY_RIGHT:
1855     case EL_BD_BUTTERFLY_UP:
1856     case EL_BD_BUTTERFLY_LEFT:
1857     case EL_BD_BUTTERFLY_DOWN:
1858     case EL_BD_FIREFLY:
1859     case EL_BD_FIREFLY_RIGHT:
1860     case EL_BD_FIREFLY_UP:
1861     case EL_BD_FIREFLY_LEFT:
1862     case EL_BD_FIREFLY_DOWN:
1863     case EL_PACMAN_RIGHT:
1864     case EL_PACMAN_UP:
1865     case EL_PACMAN_LEFT:
1866     case EL_PACMAN_DOWN:
1867     case EL_YAMYAM:
1868     case EL_YAMYAM_LEFT:
1869     case EL_YAMYAM_RIGHT:
1870     case EL_YAMYAM_UP:
1871     case EL_YAMYAM_DOWN:
1872     case EL_DARK_YAMYAM:
1873     case EL_ROBOT:
1874     case EL_PACMAN:
1875     case EL_SP_SNIKSNAK:
1876     case EL_SP_ELECTRON:
1877     case EL_MOLE:
1878     case EL_MOLE_LEFT:
1879     case EL_MOLE_RIGHT:
1880     case EL_MOLE_UP:
1881     case EL_MOLE_DOWN:
1882       InitMovDir(x, y);
1883       break;
1884
1885     case EL_AMOEBA_FULL:
1886     case EL_BD_AMOEBA:
1887       InitAmoebaNr(x, y);
1888       break;
1889
1890     case EL_AMOEBA_DROP:
1891       if (y == lev_fieldy - 1)
1892       {
1893         Feld[x][y] = EL_AMOEBA_GROWING;
1894         Store[x][y] = EL_AMOEBA_WET;
1895       }
1896       break;
1897
1898     case EL_DYNAMITE_ACTIVE:
1899     case EL_SP_DISK_RED_ACTIVE:
1900     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904       MovDelay[x][y] = 96;
1905       break;
1906
1907     case EL_EM_DYNAMITE_ACTIVE:
1908       MovDelay[x][y] = 32;
1909       break;
1910
1911     case EL_LAMP:
1912       local_player->lights_still_needed++;
1913       break;
1914
1915     case EL_PENGUIN:
1916       local_player->friends_still_needed++;
1917       break;
1918
1919     case EL_PIG:
1920     case EL_DRAGON:
1921       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922       break;
1923
1924     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936       if (init_game)
1937       {
1938         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1941
1942         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1943         {
1944           game.belt_dir[belt_nr] = belt_dir;
1945           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1946         }
1947         else    /* more than one switch -- set it like the first switch */
1948         {
1949           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950         }
1951       }
1952       break;
1953
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1956       if (init_game)
1957         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958       break;
1959
1960     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1961       if (init_game)
1962         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1963       break;
1964 #endif
1965
1966     case EL_LIGHT_SWITCH_ACTIVE:
1967       if (init_game)
1968         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969       break;
1970
1971     case EL_INVISIBLE_STEELWALL:
1972     case EL_INVISIBLE_WALL:
1973     case EL_INVISIBLE_SAND:
1974       if (game.light_time_left > 0 ||
1975           game.lenses_time_left > 0)
1976         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977       break;
1978
1979     case EL_EMC_MAGIC_BALL:
1980       if (game.ball_state)
1981         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982       break;
1983
1984     case EL_EMC_MAGIC_BALL_SWITCH:
1985       if (game.ball_state)
1986         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987       break;
1988
1989     case EL_TRIGGER_PLAYER:
1990     case EL_TRIGGER_ELEMENT:
1991     case EL_TRIGGER_CE_VALUE:
1992     case EL_TRIGGER_CE_SCORE:
1993     case EL_SELF:
1994     case EL_ANY_ELEMENT:
1995     case EL_CURRENT_CE_VALUE:
1996     case EL_CURRENT_CE_SCORE:
1997     case EL_PREV_CE_1:
1998     case EL_PREV_CE_2:
1999     case EL_PREV_CE_3:
2000     case EL_PREV_CE_4:
2001     case EL_PREV_CE_5:
2002     case EL_PREV_CE_6:
2003     case EL_PREV_CE_7:
2004     case EL_PREV_CE_8:
2005     case EL_NEXT_CE_1:
2006     case EL_NEXT_CE_2:
2007     case EL_NEXT_CE_3:
2008     case EL_NEXT_CE_4:
2009     case EL_NEXT_CE_5:
2010     case EL_NEXT_CE_6:
2011     case EL_NEXT_CE_7:
2012     case EL_NEXT_CE_8:
2013       /* reference elements should not be used on the playfield */
2014       Feld[x][y] = EL_EMPTY;
2015       break;
2016
2017     default:
2018       if (IS_CUSTOM_ELEMENT(element))
2019       {
2020         if (CAN_MOVE(element))
2021           InitMovDir(x, y);
2022
2023 #if USE_NEW_CUSTOM_VALUE
2024         if (!element_info[element].use_last_ce_value || init_game)
2025           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 #endif
2027       }
2028       else if (IS_GROUP_ELEMENT(element))
2029       {
2030         Feld[x][y] = GetElementFromGroupElement(element);
2031
2032         InitField(x, y, init_game);
2033       }
2034
2035       break;
2036   }
2037
2038   if (!init_game)
2039     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 }
2041
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2043 {
2044   InitField(x, y, init_game);
2045
2046   /* not needed to call InitMovDir() -- already done by InitField()! */
2047   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048       CAN_MOVE(Feld[x][y]))
2049     InitMovDir(x, y);
2050 }
2051
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2053 {
2054   int old_element = Feld[x][y];
2055
2056   InitField(x, y, init_game);
2057
2058   /* not needed to call InitMovDir() -- already done by InitField()! */
2059   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060       CAN_MOVE(old_element) &&
2061       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062     InitMovDir(x, y);
2063
2064   /* this case is in fact a combination of not less than three bugs:
2065      first, it calls InitMovDir() for elements that can move, although this is
2066      already done by InitField(); then, it checks the element that was at this
2067      field _before_ the call to InitField() (which can change it); lastly, it
2068      was not called for "mole with direction" elements, which were treated as
2069      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070   */
2071 }
2072
2073 #if 1
2074
2075 static int get_key_element_from_nr(int key_nr)
2076 {
2077   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079                           EL_EM_KEY_1 : EL_KEY_1);
2080
2081   return key_base_element + key_nr;
2082 }
2083
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2085 {
2086   return (player->inventory_size > 0 ?
2087           player->inventory_element[player->inventory_size - 1] :
2088           player->inventory_infinite_element != EL_UNDEFINED ?
2089           player->inventory_infinite_element :
2090           player->dynabombs_left > 0 ?
2091           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2092           EL_UNDEFINED);
2093 }
2094
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2096 {
2097   /* pos >= 0: get element from bottom of the stack;
2098      pos <  0: get element from top of the stack */
2099
2100   if (pos < 0)
2101   {
2102     int min_inventory_size = -pos;
2103     int inventory_pos = player->inventory_size - min_inventory_size;
2104     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2105
2106     return (player->inventory_size >= min_inventory_size ?
2107             player->inventory_element[inventory_pos] :
2108             player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             EL_UNDEFINED);
2113   }
2114   else
2115   {
2116     int min_dynabombs_left = pos + 1;
2117     int min_inventory_size = pos + 1 - player->dynabombs_left;
2118     int inventory_pos = pos - player->dynabombs_left;
2119
2120     return (player->inventory_infinite_element != EL_UNDEFINED ?
2121             player->inventory_infinite_element :
2122             player->dynabombs_left >= min_dynabombs_left ?
2123             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124             player->inventory_size >= min_inventory_size ?
2125             player->inventory_element[inventory_pos] :
2126             EL_UNDEFINED);
2127   }
2128 }
2129
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2131 {
2132   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134   int compare_result;
2135
2136   if (gpo1->sort_priority != gpo2->sort_priority)
2137     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2138   else
2139     compare_result = gpo1->nr - gpo2->nr;
2140
2141   return compare_result;
2142 }
2143
2144 void InitGameControlValues()
2145 {
2146   int i;
2147
2148   for (i = 0; game_panel_controls[i].nr != -1; i++)
2149   {
2150     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152     struct TextPosInfo *pos = gpc->pos;
2153     int nr = gpc->nr;
2154     int type = gpc->type;
2155
2156     if (nr != i)
2157     {
2158       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159       Error(ERR_EXIT, "this should not happen -- please debug");
2160     }
2161
2162     /* force update of game controls after initialization */
2163     gpc->value = gpc->last_value = -1;
2164     gpc->frame = gpc->last_frame = -1;
2165     gpc->gfx_frame = -1;
2166
2167     /* determine panel value width for later calculation of alignment */
2168     if (type == TYPE_INTEGER || type == TYPE_STRING)
2169     {
2170       pos->width = pos->size * getFontWidth(pos->font);
2171       pos->height = getFontHeight(pos->font);
2172     }
2173     else if (type == TYPE_ELEMENT)
2174     {
2175       pos->width = pos->size;
2176       pos->height = pos->size;
2177     }
2178
2179     /* fill structure for game panel draw order */
2180     gpo->nr = gpc->nr;
2181     gpo->sort_priority = pos->sort_priority;
2182   }
2183
2184   /* sort game panel controls according to sort_priority and control number */
2185   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 }
2188
2189 void UpdatePlayfieldElementCount()
2190 {
2191   boolean use_element_count = FALSE;
2192   int i, j, x, y;
2193
2194   /* first check if it is needed at all to calculate playfield element count */
2195   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197       use_element_count = TRUE;
2198
2199   if (!use_element_count)
2200     return;
2201
2202   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203     element_info[i].element_count = 0;
2204
2205   SCAN_PLAYFIELD(x, y)
2206   {
2207     element_info[Feld[x][y]].element_count++;
2208   }
2209
2210   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212       if (IS_IN_GROUP(j, i))
2213         element_info[EL_GROUP_START + i].element_count +=
2214           element_info[j].element_count;
2215 }
2216
2217 void UpdateGameControlValues()
2218 {
2219   int i, k;
2220   int time = (local_player->LevelSolved ?
2221               local_player->LevelSolved_CountingTime :
2222               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223               level.native_em_level->lev->time :
2224               level.time == 0 ? TimePlayed : TimeLeft);
2225   int score = (local_player->LevelSolved ?
2226                local_player->LevelSolved_CountingScore :
2227                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228                level.native_em_level->lev->score :
2229                local_player->score);
2230   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231               level.native_em_level->lev->required :
2232               local_player->gems_still_needed);
2233   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234                      level.native_em_level->lev->required > 0 :
2235                      local_player->gems_still_needed > 0 ||
2236                      local_player->sokobanfields_still_needed > 0 ||
2237                      local_player->lights_still_needed > 0);
2238
2239   UpdatePlayfieldElementCount();
2240
2241   /* update game panel control values */
2242
2243   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2245
2246   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247   for (i = 0; i < MAX_NUM_KEYS; i++)
2248     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2251
2252   if (game.centered_player_nr == -1)
2253   {
2254     for (i = 0; i < MAX_PLAYERS; i++)
2255     {
2256       for (k = 0; k < MAX_NUM_KEYS; k++)
2257       {
2258         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2259         {
2260           if (level.native_em_level->ply[i]->keys & (1 << k))
2261             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262               get_key_element_from_nr(k);
2263         }
2264         else if (stored_player[i].key[k])
2265           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266             get_key_element_from_nr(k);
2267       }
2268
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271           level.native_em_level->ply[i]->dynamite;
2272       else
2273         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274           stored_player[i].inventory_size;
2275
2276       if (stored_player[i].num_white_keys > 0)
2277         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278           EL_DC_KEY_WHITE;
2279
2280       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281         stored_player[i].num_white_keys;
2282     }
2283   }
2284   else
2285   {
2286     int player_nr = game.centered_player_nr;
2287
2288     for (k = 0; k < MAX_NUM_KEYS; k++)
2289     {
2290       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291       {
2292         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296       else if (stored_player[player_nr].key[k])
2297         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298           get_key_element_from_nr(k);
2299     }
2300
2301     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303         level.native_em_level->ply[player_nr]->dynamite;
2304     else
2305       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306         stored_player[player_nr].inventory_size;
2307
2308     if (stored_player[player_nr].num_white_keys > 0)
2309       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2310
2311     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312       stored_player[player_nr].num_white_keys;
2313   }
2314
2315   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2316   {
2317     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318       get_inventory_element_from_pos(local_player, i);
2319     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320       get_inventory_element_from_pos(local_player, -i - 1);
2321   }
2322
2323   game_panel_controls[GAME_PANEL_SCORE].value = score;
2324   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2325
2326   game_panel_controls[GAME_PANEL_TIME].value = time;
2327
2328   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2331
2332   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2334      EL_EMPTY);
2335   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336     local_player->shield_normal_time_left;
2337   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2339      EL_EMPTY);
2340   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341     local_player->shield_deadly_time_left;
2342
2343   game_panel_controls[GAME_PANEL_EXIT].value =
2344     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2345
2346   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350      EL_EMC_MAGIC_BALL_SWITCH);
2351
2352   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355     game.light_time_left;
2356
2357   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360     game.timegate_time_left;
2361
2362   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2364
2365   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368     game.lenses_time_left;
2369
2370   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373     game.magnify_time_left;
2374
2375   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2377      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2379      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2380      EL_BALLOON_SWITCH_NONE);
2381
2382   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383     local_player->dynabomb_count;
2384   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385     local_player->dynabomb_size;
2386   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2388
2389   game_panel_controls[GAME_PANEL_PENGUINS].value =
2390     local_player->friends_still_needed;
2391
2392   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393     local_player->sokobanfields_still_needed;
2394   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395     local_player->sokobanfields_still_needed;
2396
2397   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2399
2400   for (i = 0; i < NUM_BELTS; i++)
2401   {
2402     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407   }
2408
2409   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412     game.magic_wall_time_left;
2413
2414 #if USE_PLAYER_GRAVITY
2415   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416     local_player->gravity;
2417 #else
2418   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 #endif
2420
2421   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2423
2424   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427        game.panel.element[i].id : EL_UNDEFINED);
2428
2429   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432        element_info[game.panel.element_count[i].id].element_count : 0);
2433
2434   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437        element_info[game.panel.ce_score[i].id].collect_score : 0);
2438
2439   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442        element_info[game.panel.ce_score_element[i].id].collect_score :
2443        EL_UNDEFINED);
2444
2445   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2448
2449   /* update game panel control frames */
2450
2451   for (i = 0; game_panel_controls[i].nr != -1; i++)
2452   {
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2454
2455     if (gpc->type == TYPE_ELEMENT)
2456     {
2457       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2458       {
2459         int last_anim_random_frame = gfx.anim_random_frame;
2460         int element = gpc->value;
2461         int graphic = el2panelimg(element);
2462
2463         if (gpc->value != gpc->last_value)
2464         {
2465           gpc->gfx_frame = 0;
2466           gpc->gfx_random = INIT_GFX_RANDOM();
2467         }
2468         else
2469         {
2470           gpc->gfx_frame++;
2471
2472           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474             gpc->gfx_random = INIT_GFX_RANDOM();
2475         }
2476
2477         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478           gfx.anim_random_frame = gpc->gfx_random;
2479
2480         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481           gpc->gfx_frame = element_info[element].collect_score;
2482
2483         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484                                               gpc->gfx_frame);
2485
2486         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487           gfx.anim_random_frame = last_anim_random_frame;
2488       }
2489     }
2490   }
2491 }
2492
2493 void DisplayGameControlValues()
2494 {
2495   boolean redraw_panel = FALSE;
2496   int i;
2497
2498   for (i = 0; game_panel_controls[i].nr != -1; i++)
2499   {
2500     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2501
2502     if (PANEL_DEACTIVATED(gpc->pos))
2503       continue;
2504
2505     if (gpc->value == gpc->last_value &&
2506         gpc->frame == gpc->last_frame)
2507       continue;
2508
2509     redraw_panel = TRUE;
2510   }
2511
2512   if (!redraw_panel)
2513     return;
2514
2515   /* copy default game door content to main double buffer */
2516   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2518
2519   /* redraw game control buttons */
2520 #if 1
2521   RedrawGameButtons();
2522 #else
2523   UnmapGameButtons();
2524   MapGameButtons();
2525 #endif
2526
2527   game_status = GAME_MODE_PSEUDO_PANEL;
2528
2529 #if 1
2530   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2531 #else
2532   for (i = 0; game_panel_controls[i].nr != -1; i++)
2533 #endif
2534   {
2535 #if 1
2536     int nr = game_panel_order[i].nr;
2537     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2538 #else
2539     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540     int nr = gpc->nr;
2541 #endif
2542     struct TextPosInfo *pos = gpc->pos;
2543     int type = gpc->type;
2544     int value = gpc->value;
2545     int frame = gpc->frame;
2546 #if 0
2547     int last_value = gpc->last_value;
2548     int last_frame = gpc->last_frame;
2549 #endif
2550     int size = pos->size;
2551     int font = pos->font;
2552     boolean draw_masked = pos->draw_masked;
2553     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2554
2555     if (PANEL_DEACTIVATED(pos))
2556       continue;
2557
2558 #if 0
2559     if (value == last_value && frame == last_frame)
2560       continue;
2561 #endif
2562
2563     gpc->last_value = value;
2564     gpc->last_frame = frame;
2565
2566 #if 0
2567     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 #endif
2569
2570     if (type == TYPE_INTEGER)
2571     {
2572       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573           nr == GAME_PANEL_TIME)
2574       {
2575         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2576
2577         if (use_dynamic_size)           /* use dynamic number of digits */
2578         {
2579           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581           int size2 = size1 + 1;
2582           int font1 = pos->font;
2583           int font2 = pos->font_alt;
2584
2585           size = (value < value_change ? size1 : size2);
2586           font = (value < value_change ? font1 : font2);
2587
2588 #if 0
2589           /* clear background if value just changed its size (dynamic digits) */
2590           if ((last_value < value_change) != (value < value_change))
2591           {
2592             int width1 = size1 * getFontWidth(font1);
2593             int width2 = size2 * getFontWidth(font2);
2594             int max_width = MAX(width1, width2);
2595             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2596
2597             pos->width = max_width;
2598
2599             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600                                        max_width, max_height);
2601           }
2602 #endif
2603         }
2604       }
2605
2606 #if 1
2607       /* correct text size if "digits" is zero or less */
2608       if (size <= 0)
2609         size = strlen(int2str(value, size));
2610
2611       /* dynamically correct text alignment */
2612       pos->width = size * getFontWidth(font);
2613 #endif
2614
2615       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616                   int2str(value, size), font, mask_mode);
2617     }
2618     else if (type == TYPE_ELEMENT)
2619     {
2620       int element, graphic;
2621       Bitmap *src_bitmap;
2622       int src_x, src_y;
2623       int width, height;
2624       int dst_x = PANEL_XPOS(pos);
2625       int dst_y = PANEL_YPOS(pos);
2626
2627 #if 1
2628       if (value != EL_UNDEFINED && value != EL_EMPTY)
2629       {
2630         element = value;
2631         graphic = el2panelimg(value);
2632
2633         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634
2635 #if 1
2636         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2637           size = TILESIZE;
2638 #endif
2639
2640         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641                               &src_x, &src_y);
2642
2643         width  = graphic_info[graphic].width  * size / TILESIZE;
2644         height = graphic_info[graphic].height * size / TILESIZE;
2645
2646         if (draw_masked)
2647         {
2648           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649                         dst_x - src_x, dst_y - src_y);
2650           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651                            dst_x, dst_y);
2652         }
2653         else
2654         {
2655           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656                      dst_x, dst_y);
2657         }
2658       }
2659 #else
2660       if (value == EL_UNDEFINED || value == EL_EMPTY)
2661       {
2662         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663         graphic = el2panelimg(element);
2664
2665         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668       }
2669       else
2670       {
2671         element = value;
2672         graphic = el2panelimg(value);
2673
2674         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675       }
2676
2677       width  = graphic_info[graphic].width  * size / TILESIZE;
2678       height = graphic_info[graphic].height * size / TILESIZE;
2679
2680       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 #endif
2682     }
2683     else if (type == TYPE_STRING)
2684     {
2685       boolean active = (value != 0);
2686       char *state_normal = "off";
2687       char *state_active = "on";
2688       char *state = (active ? state_active : state_normal);
2689       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2691                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2692                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2693
2694       if (nr == GAME_PANEL_GRAVITY_STATE)
2695       {
2696         int font1 = pos->font;          /* (used for normal state) */
2697         int font2 = pos->font_alt;      /* (used for active state) */
2698 #if 0
2699         int size1 = strlen(state_normal);
2700         int size2 = strlen(state_active);
2701         int width1 = size1 * getFontWidth(font1);
2702         int width2 = size2 * getFontWidth(font2);
2703         int max_width = MAX(width1, width2);
2704         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2705
2706         pos->width = max_width;
2707
2708         /* clear background for values that may have changed its size */
2709         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710                                    max_width, max_height);
2711 #endif
2712
2713         font = (active ? font2 : font1);
2714       }
2715
2716       if (s != NULL)
2717       {
2718         char *s_cut;
2719
2720 #if 1
2721         if (size <= 0)
2722         {
2723           /* don't truncate output if "chars" is zero or less */
2724           size = strlen(s);
2725
2726           /* dynamically correct text alignment */
2727           pos->width = size * getFontWidth(font);
2728         }
2729 #endif
2730
2731         s_cut = getStringCopyN(s, size);
2732
2733         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734                     s_cut, font, mask_mode);
2735
2736         free(s_cut);
2737       }
2738     }
2739
2740     redraw_mask |= REDRAW_DOOR_1;
2741   }
2742
2743   game_status = GAME_MODE_PLAYING;
2744 }
2745
2746 void UpdateAndDisplayGameControlValues()
2747 {
2748   if (tape.warp_forward)
2749     return;
2750
2751   UpdateGameControlValues();
2752   DisplayGameControlValues();
2753 }
2754
2755 void DrawGameValue_Emeralds(int value)
2756 {
2757   struct TextPosInfo *pos = &game.panel.gems;
2758 #if 1
2759   int font_nr = pos->font;
2760 #else
2761   int font_nr = FONT_TEXT_2;
2762 #endif
2763   int font_width = getFontWidth(font_nr);
2764   int chars = pos->size;
2765
2766 #if 1
2767   return;       /* !!! USE NEW STUFF !!! */
2768 #endif
2769
2770   if (PANEL_DEACTIVATED(pos))
2771     return;
2772
2773   pos->width = chars * font_width;
2774
2775   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 }
2777
2778 void DrawGameValue_Dynamite(int value)
2779 {
2780   struct TextPosInfo *pos = &game.panel.inventory_count;
2781 #if 1
2782   int font_nr = pos->font;
2783 #else
2784   int font_nr = FONT_TEXT_2;
2785 #endif
2786   int font_width = getFontWidth(font_nr);
2787   int chars = pos->size;
2788
2789 #if 1
2790   return;       /* !!! USE NEW STUFF !!! */
2791 #endif
2792
2793   if (PANEL_DEACTIVATED(pos))
2794     return;
2795
2796   pos->width = chars * font_width;
2797
2798   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 }
2800
2801 void DrawGameValue_Score(int value)
2802 {
2803   struct TextPosInfo *pos = &game.panel.score;
2804 #if 1
2805   int font_nr = pos->font;
2806 #else
2807   int font_nr = FONT_TEXT_2;
2808 #endif
2809   int font_width = getFontWidth(font_nr);
2810   int chars = pos->size;
2811
2812 #if 1
2813   return;       /* !!! USE NEW STUFF !!! */
2814 #endif
2815
2816   if (PANEL_DEACTIVATED(pos))
2817     return;
2818
2819   pos->width = chars * font_width;
2820
2821   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 }
2823
2824 void DrawGameValue_Time(int value)
2825 {
2826   struct TextPosInfo *pos = &game.panel.time;
2827   static int last_value = -1;
2828   int chars1 = 3;
2829   int chars2 = 4;
2830   int chars = pos->size;
2831 #if 1
2832   int font1_nr = pos->font;
2833   int font2_nr = pos->font_alt;
2834 #else
2835   int font1_nr = FONT_TEXT_2;
2836   int font2_nr = FONT_TEXT_1;
2837 #endif
2838   int font_nr = font1_nr;
2839   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840
2841 #if 1
2842   return;       /* !!! USE NEW STUFF !!! */
2843 #endif
2844
2845   if (PANEL_DEACTIVATED(pos))
2846     return;
2847
2848   if (use_dynamic_chars)                /* use dynamic number of chars */
2849   {
2850     chars   = (value < 1000 ? chars1   : chars2);
2851     font_nr = (value < 1000 ? font1_nr : font2_nr);
2852   }
2853
2854   /* clear background if value just changed its size (dynamic chars only) */
2855   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2856   {
2857     int width1 = chars1 * getFontWidth(font1_nr);
2858     int width2 = chars2 * getFontWidth(font2_nr);
2859     int max_width = MAX(width1, width2);
2860     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2861
2862     pos->width = max_width;
2863
2864     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865                                max_width, max_height);
2866   }
2867
2868   pos->width = chars * getFontWidth(font_nr);
2869
2870   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871
2872   last_value = value;
2873 }
2874
2875 void DrawGameValue_Level(int value)
2876 {
2877   struct TextPosInfo *pos = &game.panel.level_number;
2878   int chars1 = 2;
2879   int chars2 = 3;
2880   int chars = pos->size;
2881 #if 1
2882   int font1_nr = pos->font;
2883   int font2_nr = pos->font_alt;
2884 #else
2885   int font1_nr = FONT_TEXT_2;
2886   int font2_nr = FONT_TEXT_1;
2887 #endif
2888   int font_nr = font1_nr;
2889   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890
2891 #if 1
2892   return;       /* !!! USE NEW STUFF !!! */
2893 #endif
2894
2895   if (PANEL_DEACTIVATED(pos))
2896     return;
2897
2898   if (use_dynamic_chars)                /* use dynamic number of chars */
2899   {
2900     chars   = (level_nr < 100 ? chars1   : chars2);
2901     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902   }
2903
2904   pos->width = chars * getFontWidth(font_nr);
2905
2906   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 }
2908
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 {
2911 #if 0
2912   struct TextPosInfo *pos = &game.panel.keys;
2913 #endif
2914 #if 0
2915   int base_key_graphic = EL_KEY_1;
2916 #endif
2917   int i;
2918
2919 #if 1
2920   return;       /* !!! USE NEW STUFF !!! */
2921 #endif
2922
2923 #if 0
2924   if (PANEL_DEACTIVATED(pos))
2925     return;
2926 #endif
2927
2928 #if 0
2929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930     base_key_graphic = EL_EM_KEY_1;
2931 #endif
2932
2933 #if 0
2934   pos->width = 4 * MINI_TILEX;
2935 #endif
2936
2937 #if 1
2938   for (i = 0; i < MAX_NUM_KEYS; i++)
2939 #else
2940   /* currently only 4 of 8 possible keys are displayed */
2941   for (i = 0; i < STD_NUM_KEYS; i++)
2942 #endif
2943   {
2944 #if 1
2945     struct TextPosInfo *pos = &game.panel.key[i];
2946 #endif
2947     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948     int src_y = DOOR_GFX_PAGEY1 + 123;
2949 #if 1
2950     int dst_x = PANEL_XPOS(pos);
2951     int dst_y = PANEL_YPOS(pos);
2952 #else
2953     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954     int dst_y = PANEL_YPOS(pos);
2955 #endif
2956
2957 #if 1
2958     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2960                    EL_KEY_1) + i;
2961     int graphic = el2edimg(element);
2962 #endif
2963
2964 #if 1
2965     if (PANEL_DEACTIVATED(pos))
2966       continue;
2967 #endif
2968
2969 #if 0
2970     /* masked blit with tiles from half-size scaled bitmap does not work yet
2971        (no mask bitmap created for these sizes after loading and scaling) --
2972        solution: load without creating mask, scale, then create final mask */
2973
2974     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976
2977     if (key[i])
2978     {
2979 #if 0
2980       int graphic = el2edimg(base_key_graphic + i);
2981 #endif
2982       Bitmap *src_bitmap;
2983       int src_x, src_y;
2984
2985       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2986
2987       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988                     dst_x - src_x, dst_y - src_y);
2989       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990                        dst_x, dst_y);
2991     }
2992 #else
2993 #if 1
2994     if (key[i])
2995       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2996     else
2997       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 #else
3000     if (key[i])
3001       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3002     else
3003       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3005 #endif
3006 #endif
3007   }
3008 }
3009
3010 #else
3011
3012 void DrawGameValue_Emeralds(int value)
3013 {
3014   int font_nr = FONT_TEXT_2;
3015   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3016
3017   if (PANEL_DEACTIVATED(game.panel.gems))
3018     return;
3019
3020   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 }
3022
3023 void DrawGameValue_Dynamite(int value)
3024 {
3025   int font_nr = FONT_TEXT_2;
3026   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3027
3028   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029     return;
3030
3031   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 }
3033
3034 void DrawGameValue_Score(int value)
3035 {
3036   int font_nr = FONT_TEXT_2;
3037   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3038
3039   if (PANEL_DEACTIVATED(game.panel.score))
3040     return;
3041
3042   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 }
3044
3045 void DrawGameValue_Time(int value)
3046 {
3047   int font1_nr = FONT_TEXT_2;
3048 #if 1
3049   int font2_nr = FONT_TEXT_1;
3050 #else
3051   int font2_nr = FONT_LEVEL_NUMBER;
3052 #endif
3053   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3055
3056   if (PANEL_DEACTIVATED(game.panel.time))
3057     return;
3058
3059   /* clear background if value just changed its size */
3060   if (value == 999 || value == 1000)
3061     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062
3063   if (value < 1000)
3064     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3065   else
3066     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 }
3068
3069 void DrawGameValue_Level(int value)
3070 {
3071   int font1_nr = FONT_TEXT_2;
3072 #if 1
3073   int font2_nr = FONT_TEXT_1;
3074 #else
3075   int font2_nr = FONT_LEVEL_NUMBER;
3076 #endif
3077
3078   if (PANEL_DEACTIVATED(game.panel.level))
3079     return;
3080
3081   if (level_nr < 100)
3082     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3083   else
3084     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 }
3086
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3088 {
3089   int base_key_graphic = EL_KEY_1;
3090   int i;
3091
3092   if (PANEL_DEACTIVATED(game.panel.keys))
3093     return;
3094
3095   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096     base_key_graphic = EL_EM_KEY_1;
3097
3098   /* currently only 4 of 8 possible keys are displayed */
3099   for (i = 0; i < STD_NUM_KEYS; i++)
3100   {
3101     int x = XX_KEYS + i * MINI_TILEX;
3102     int y = YY_KEYS;
3103
3104     if (key[i])
3105       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3106     else
3107       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109   }
3110 }
3111
3112 #endif
3113
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115                        int key_bits)
3116 {
3117   int key[MAX_NUM_KEYS];
3118   int i;
3119
3120   /* prevent EM engine from updating time/score values parallel to GameWon() */
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122       local_player->LevelSolved)
3123     return;
3124
3125   for (i = 0; i < MAX_NUM_KEYS; i++)
3126     key[i] = key_bits & (1 << i);
3127
3128   DrawGameValue_Level(level_nr);
3129
3130   DrawGameValue_Emeralds(emeralds);
3131   DrawGameValue_Dynamite(dynamite);
3132   DrawGameValue_Score(score);
3133   DrawGameValue_Time(time);
3134
3135   DrawGameValue_Keys(key);
3136 }
3137
3138 void UpdateGameDoorValues()
3139 {
3140   UpdateGameControlValues();
3141 }
3142
3143 void DrawGameDoorValues()
3144 {
3145   DisplayGameControlValues();
3146 }
3147
3148 void DrawGameDoorValues_OLD()
3149 {
3150   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151   int dynamite_value = 0;
3152   int score_value = (local_player->LevelSolved ? local_player->score_final :
3153                      local_player->score);
3154   int gems_value = local_player->gems_still_needed;
3155   int key_bits = 0;
3156   int i, j;
3157
3158   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3159   {
3160     DrawGameDoorValues_EM();
3161
3162     return;
3163   }
3164
3165   if (game.centered_player_nr == -1)
3166   {
3167     for (i = 0; i < MAX_PLAYERS; i++)
3168     {
3169       for (j = 0; j < MAX_NUM_KEYS; j++)
3170         if (stored_player[i].key[j])
3171           key_bits |= (1 << j);
3172
3173       dynamite_value += stored_player[i].inventory_size;
3174     }
3175   }
3176   else
3177   {
3178     int player_nr = game.centered_player_nr;
3179
3180     for (i = 0; i < MAX_NUM_KEYS; i++)
3181       if (stored_player[player_nr].key[i])
3182         key_bits |= (1 << i);
3183
3184     dynamite_value = stored_player[player_nr].inventory_size;
3185   }
3186
3187   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188                     key_bits);
3189 }
3190
3191
3192 /*
3193   =============================================================================
3194   InitGameEngine()
3195   -----------------------------------------------------------------------------
3196   initialize game engine due to level / tape version number
3197   =============================================================================
3198 */
3199
3200 static void InitGameEngine()
3201 {
3202   int i, j, k, l, x, y;
3203
3204   /* set game engine from tape file when re-playing, else from level file */
3205   game.engine_version = (tape.playing ? tape.engine_version :
3206                          level.game_version);
3207
3208   /* ---------------------------------------------------------------------- */
3209   /* set flags for bugs and changes according to active game engine version */
3210   /* ---------------------------------------------------------------------- */
3211
3212   /*
3213     Summary of bugfix/change:
3214     Fixed handling for custom elements that change when pushed by the player.
3215
3216     Fixed/changed in version:
3217     3.1.0
3218
3219     Description:
3220     Before 3.1.0, custom elements that "change when pushing" changed directly
3221     after the player started pushing them (until then handled in "DigField()").
3222     Since 3.1.0, these custom elements are not changed until the "pushing"
3223     move of the element is finished (now handled in "ContinueMoving()").
3224
3225     Affected levels/tapes:
3226     The first condition is generally needed for all levels/tapes before version
3227     3.1.0, which might use the old behaviour before it was changed; known tapes
3228     that are affected are some tapes from the level set "Walpurgis Gardens" by
3229     Jamie Cullen.
3230     The second condition is an exception from the above case and is needed for
3231     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232     above (including some development versions of 3.1.0), but before it was
3233     known that this change would break tapes like the above and was fixed in
3234     3.1.1, so that the changed behaviour was active although the engine version
3235     while recording maybe was before 3.1.0. There is at least one tape that is
3236     affected by this exception, which is the tape for the one-level set "Bug
3237     Machine" by Juergen Bonhagen.
3238   */
3239
3240   game.use_change_when_pushing_bug =
3241     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3242      !(tape.playing &&
3243        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3245
3246   /*
3247     Summary of bugfix/change:
3248     Fixed handling for blocking the field the player leaves when moving.
3249
3250     Fixed/changed in version:
3251     3.1.1
3252
3253     Description:
3254     Before 3.1.1, when "block last field when moving" was enabled, the field
3255     the player is leaving when moving was blocked for the time of the move,
3256     and was directly unblocked afterwards. This resulted in the last field
3257     being blocked for exactly one less than the number of frames of one player
3258     move. Additionally, even when blocking was disabled, the last field was
3259     blocked for exactly one frame.
3260     Since 3.1.1, due to changes in player movement handling, the last field
3261     is not blocked at all when blocking is disabled. When blocking is enabled,
3262     the last field is blocked for exactly the number of frames of one player
3263     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264     last field is blocked for exactly one more than the number of frames of
3265     one player move.
3266
3267     Affected levels/tapes:
3268     (!!! yet to be determined -- probably many !!!)
3269   */
3270
3271   game.use_block_last_field_bug =
3272     (game.engine_version < VERSION_IDENT(3,1,1,0));
3273
3274   /*
3275     Summary of bugfix/change:
3276     Changed behaviour of CE changes with multiple changes per single frame.
3277
3278     Fixed/changed in version:
3279     3.2.0-6
3280
3281     Description:
3282     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283     This resulted in race conditions where CEs seem to behave strange in some
3284     situations (where triggered CE changes were just skipped because there was
3285     already a CE change on that tile in the playfield in that engine frame).
3286     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287     (The number of changes per frame must be limited in any case, because else
3288     it is easily possible to define CE changes that would result in an infinite
3289     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290     should be set large enough so that it would only be reached in cases where
3291     the corresponding CE change conditions run into a loop. Therefore, it seems
3292     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293     maximal number of change pages for custom elements.)
3294
3295     Affected levels/tapes:
3296     Probably many.
3297   */
3298
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300   game.max_num_changes_per_frame = 1;
3301 #else
3302   game.max_num_changes_per_frame =
3303     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 #endif
3305
3306   /* ---------------------------------------------------------------------- */
3307
3308   /* default scan direction: scan playfield from top/left to bottom/right */
3309   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3310
3311   /* dynamically adjust element properties according to game engine version */
3312   InitElementPropertiesEngine(game.engine_version);
3313
3314 #if 0
3315   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316   printf("          tape version == %06d [%s] [file: %06d]\n",
3317          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3318          tape.file_version);
3319   printf("       => game.engine_version == %06d\n", game.engine_version);
3320 #endif
3321
3322   /* ---------- initialize player's initial move delay --------------------- */
3323
3324   /* dynamically adjust player properties according to level information */
3325   for (i = 0; i < MAX_PLAYERS; i++)
3326     game.initial_move_delay_value[i] =
3327       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3328
3329   /* dynamically adjust player properties according to game engine version */
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331     game.initial_move_delay[i] =
3332       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333        game.initial_move_delay_value[i] : 0);
3334
3335   /* ---------- initialize player's initial push delay --------------------- */
3336
3337   /* dynamically adjust player properties according to game engine version */
3338   game.initial_push_delay_value =
3339     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3340
3341   /* ---------- initialize changing elements ------------------------------- */
3342
3343   /* initialize changing elements information */
3344   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3345   {
3346     struct ElementInfo *ei = &element_info[i];
3347
3348     /* this pointer might have been changed in the level editor */
3349     ei->change = &ei->change_page[0];
3350
3351     if (!IS_CUSTOM_ELEMENT(i))
3352     {
3353       ei->change->target_element = EL_EMPTY_SPACE;
3354       ei->change->delay_fixed = 0;
3355       ei->change->delay_random = 0;
3356       ei->change->delay_frames = 1;
3357     }
3358
3359     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3360     {
3361       ei->has_change_event[j] = FALSE;
3362
3363       ei->event_page_nr[j] = 0;
3364       ei->event_page[j] = &ei->change_page[0];
3365     }
3366   }
3367
3368   /* add changing elements from pre-defined list */
3369   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3370   {
3371     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372     struct ElementInfo *ei = &element_info[ch_delay->element];
3373
3374     ei->change->target_element       = ch_delay->target_element;
3375     ei->change->delay_fixed          = ch_delay->change_delay;
3376
3377     ei->change->pre_change_function  = ch_delay->pre_change_function;
3378     ei->change->change_function      = ch_delay->change_function;
3379     ei->change->post_change_function = ch_delay->post_change_function;
3380
3381     ei->change->can_change = TRUE;
3382     ei->change->can_change_or_has_action = TRUE;
3383
3384     ei->has_change_event[CE_DELAY] = TRUE;
3385
3386     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388   }
3389
3390   /* ---------- initialize internal run-time variables --------------------- */
3391
3392   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3393   {
3394     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3395
3396     for (j = 0; j < ei->num_change_pages; j++)
3397     {
3398       ei->change_page[j].can_change_or_has_action =
3399         (ei->change_page[j].can_change |
3400          ei->change_page[j].has_action);
3401     }
3402   }
3403
3404   /* add change events from custom element configuration */
3405   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3406   {
3407     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3408
3409     for (j = 0; j < ei->num_change_pages; j++)
3410     {
3411       if (!ei->change_page[j].can_change_or_has_action)
3412         continue;
3413
3414       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3415       {
3416         /* only add event page for the first page found with this event */
3417         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3418         {
3419           ei->has_change_event[k] = TRUE;
3420
3421           ei->event_page_nr[k] = j;
3422           ei->event_page[k] = &ei->change_page[j];
3423         }
3424       }
3425     }
3426   }
3427
3428 #if 1
3429   /* ---------- initialize reference elements in change conditions --------- */
3430
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     int element = EL_CUSTOM_START + i;
3434     struct ElementInfo *ei = &element_info[element];
3435
3436     for (j = 0; j < ei->num_change_pages; j++)
3437     {
3438       int trigger_element = ei->change_page[j].initial_trigger_element;
3439
3440       if (trigger_element >= EL_PREV_CE_8 &&
3441           trigger_element <= EL_NEXT_CE_8)
3442         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3443
3444       ei->change_page[j].trigger_element = trigger_element;
3445     }
3446   }
3447 #endif
3448
3449   /* ---------- initialize run-time trigger player and element ------------- */
3450
3451   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3452   {
3453     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3454
3455     for (j = 0; j < ei->num_change_pages; j++)
3456     {
3457       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461       ei->change_page[j].actual_trigger_ce_value = 0;
3462       ei->change_page[j].actual_trigger_ce_score = 0;
3463     }
3464   }
3465
3466   /* ---------- initialize trigger events ---------------------------------- */
3467
3468   /* initialize trigger events information */
3469   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471       trigger_events[i][j] = FALSE;
3472
3473   /* add trigger events from element change event properties */
3474   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475   {
3476     struct ElementInfo *ei = &element_info[i];
3477
3478     for (j = 0; j < ei->num_change_pages; j++)
3479     {
3480       if (!ei->change_page[j].can_change_or_has_action)
3481         continue;
3482
3483       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3484       {
3485         int trigger_element = ei->change_page[j].trigger_element;
3486
3487         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3488         {
3489           if (ei->change_page[j].has_event[k])
3490           {
3491             if (IS_GROUP_ELEMENT(trigger_element))
3492             {
3493               struct ElementGroupInfo *group =
3494                 element_info[trigger_element].group;
3495
3496               for (l = 0; l < group->num_elements_resolved; l++)
3497                 trigger_events[group->element_resolved[l]][k] = TRUE;
3498             }
3499             else if (trigger_element == EL_ANY_ELEMENT)
3500               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501                 trigger_events[l][k] = TRUE;
3502             else
3503               trigger_events[trigger_element][k] = TRUE;
3504           }
3505         }
3506       }
3507     }
3508   }
3509
3510   /* ---------- initialize push delay -------------------------------------- */
3511
3512   /* initialize push delay values to default */
3513   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3514   {
3515     if (!IS_CUSTOM_ELEMENT(i))
3516     {
3517       /* set default push delay values (corrected since version 3.0.7-1) */
3518       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3519       {
3520         element_info[i].push_delay_fixed = 2;
3521         element_info[i].push_delay_random = 8;
3522       }
3523       else
3524       {
3525         element_info[i].push_delay_fixed = 8;
3526         element_info[i].push_delay_random = 8;
3527       }
3528     }
3529   }
3530
3531   /* set push delay value for certain elements from pre-defined list */
3532   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3533   {
3534     int e = push_delay_list[i].element;
3535
3536     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3537     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538   }
3539
3540   /* set push delay value for Supaplex elements for newer engine versions */
3541   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3542   {
3543     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3544     {
3545       if (IS_SP_ELEMENT(i))
3546       {
3547         /* set SP push delay to just enough to push under a falling zonk */
3548         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3549
3550         element_info[i].push_delay_fixed  = delay;
3551         element_info[i].push_delay_random = 0;
3552       }
3553     }
3554   }
3555
3556   /* ---------- initialize move stepsize ----------------------------------- */
3557
3558   /* initialize move stepsize values to default */
3559   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560     if (!IS_CUSTOM_ELEMENT(i))
3561       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3562
3563   /* set move stepsize value for certain elements from pre-defined list */
3564   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3565   {
3566     int e = move_stepsize_list[i].element;
3567
3568     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569   }
3570
3571   /* ---------- initialize collect score ----------------------------------- */
3572
3573   /* initialize collect score values for custom elements from initial value */
3574   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575     if (IS_CUSTOM_ELEMENT(i))
3576       element_info[i].collect_score = element_info[i].collect_score_initial;
3577
3578   /* ---------- initialize collect count ----------------------------------- */
3579
3580   /* initialize collect count values for non-custom elements */
3581   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582     if (!IS_CUSTOM_ELEMENT(i))
3583       element_info[i].collect_count_initial = 0;
3584
3585   /* add collect count values for all elements from pre-defined list */
3586   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587     element_info[collect_count_list[i].element].collect_count_initial =
3588       collect_count_list[i].count;
3589
3590   /* ---------- initialize access direction -------------------------------- */
3591
3592   /* initialize access direction values to default (access from every side) */
3593   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594     if (!IS_CUSTOM_ELEMENT(i))
3595       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3596
3597   /* set access direction value for certain elements from pre-defined list */
3598   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599     element_info[access_direction_list[i].element].access_direction =
3600       access_direction_list[i].direction;
3601
3602   /* ---------- initialize explosion content ------------------------------- */
3603   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3604   {
3605     if (IS_CUSTOM_ELEMENT(i))
3606       continue;
3607
3608     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3609     {
3610       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3611
3612       element_info[i].content.e[x][y] =
3613         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615          i == EL_PLAYER_3 ? EL_EMERALD :
3616          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617          i == EL_MOLE ? EL_EMERALD_RED :
3618          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623          i == EL_WALL_EMERALD ? EL_EMERALD :
3624          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629          i == EL_WALL_PEARL ? EL_PEARL :
3630          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631          EL_EMPTY);
3632     }
3633   }
3634
3635   /* ---------- initialize recursion detection ------------------------------ */
3636   recursion_loop_depth = 0;
3637   recursion_loop_detected = FALSE;
3638   recursion_loop_element = EL_UNDEFINED;
3639
3640   /* ---------- initialize graphics engine ---------------------------------- */
3641   game.scroll_delay_value =
3642     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3644   game.scroll_delay_value =
3645     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 }
3647
3648 int get_num_special_action(int element, int action_first, int action_last)
3649 {
3650   int num_special_action = 0;
3651   int i, j;
3652
3653   for (i = action_first; i <= action_last; i++)
3654   {
3655     boolean found = FALSE;
3656
3657     for (j = 0; j < NUM_DIRECTIONS; j++)
3658       if (el_act_dir2img(element, i, j) !=
3659           el_act_dir2img(element, ACTION_DEFAULT, j))
3660         found = TRUE;
3661
3662     if (found)
3663       num_special_action++;
3664     else
3665       break;
3666   }
3667
3668   return num_special_action;
3669 }
3670
3671
3672 /*
3673   =============================================================================
3674   InitGame()
3675   -----------------------------------------------------------------------------
3676   initialize and start new game
3677   =============================================================================
3678 */
3679
3680 void InitGame()
3681 {
3682   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3683   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3684   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3685 #if 0
3686   boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 #endif
3688 #if 1
3689   int initial_move_dir = MV_DOWN;
3690 #else
3691   int initial_move_dir = MV_NONE;
3692 #endif
3693   int i, j, x, y;
3694
3695   game_status = GAME_MODE_PLAYING;
3696
3697   InitGameEngine();
3698   InitGameControlValues();
3699
3700   /* don't play tapes over network */
3701   network_playing = (options.network && !tape.playing);
3702
3703   for (i = 0; i < MAX_PLAYERS; i++)
3704   {
3705     struct PlayerInfo *player = &stored_player[i];
3706
3707     player->index_nr = i;
3708     player->index_bit = (1 << i);
3709     player->element_nr = EL_PLAYER_1 + i;
3710
3711     player->present = FALSE;
3712     player->active = FALSE;
3713     player->mapped = FALSE;
3714
3715     player->killed = FALSE;
3716     player->reanimated = FALSE;
3717
3718     player->action = 0;
3719     player->effective_action = 0;
3720     player->programmed_action = 0;
3721
3722     player->score = 0;
3723     player->score_final = 0;
3724
3725     player->gems_still_needed = level.gems_needed;
3726     player->sokobanfields_still_needed = 0;
3727     player->lights_still_needed = 0;
3728     player->friends_still_needed = 0;
3729
3730     for (j = 0; j < MAX_NUM_KEYS; j++)
3731       player->key[j] = FALSE;
3732
3733     player->num_white_keys = 0;
3734
3735     player->dynabomb_count = 0;
3736     player->dynabomb_size = 1;
3737     player->dynabombs_left = 0;
3738     player->dynabomb_xl = FALSE;
3739
3740     player->MovDir = initial_move_dir;
3741     player->MovPos = 0;
3742     player->GfxPos = 0;
3743     player->GfxDir = initial_move_dir;
3744     player->GfxAction = ACTION_DEFAULT;
3745     player->Frame = 0;
3746     player->StepFrame = 0;
3747
3748     player->initial_element = player->element_nr;
3749     player->artwork_element =
3750       (level.use_artwork_element[i] ? level.artwork_element[i] :
3751        player->element_nr);
3752     player->use_murphy = FALSE;
3753
3754     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3755     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3756
3757     player->gravity = level.initial_player_gravity[i];
3758
3759     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3760
3761     player->actual_frame_counter = 0;
3762
3763     player->step_counter = 0;
3764
3765     player->last_move_dir = initial_move_dir;
3766
3767     player->is_active = FALSE;
3768
3769     player->is_waiting = FALSE;
3770     player->is_moving = FALSE;
3771     player->is_auto_moving = FALSE;
3772     player->is_digging = FALSE;
3773     player->is_snapping = FALSE;
3774     player->is_collecting = FALSE;
3775     player->is_pushing = FALSE;
3776     player->is_switching = FALSE;
3777     player->is_dropping = FALSE;
3778     player->is_dropping_pressed = FALSE;
3779
3780     player->is_bored = FALSE;
3781     player->is_sleeping = FALSE;
3782
3783     player->frame_counter_bored = -1;
3784     player->frame_counter_sleeping = -1;
3785
3786     player->anim_delay_counter = 0;
3787     player->post_delay_counter = 0;
3788
3789     player->dir_waiting = initial_move_dir;
3790     player->action_waiting = ACTION_DEFAULT;
3791     player->last_action_waiting = ACTION_DEFAULT;
3792     player->special_action_bored = ACTION_DEFAULT;
3793     player->special_action_sleeping = ACTION_DEFAULT;
3794
3795     player->switch_x = -1;
3796     player->switch_y = -1;
3797
3798     player->drop_x = -1;
3799     player->drop_y = -1;
3800
3801     player->show_envelope = 0;
3802
3803     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3804
3805     player->push_delay       = -1;      /* initialized when pushing starts */
3806     player->push_delay_value = game.initial_push_delay_value;
3807
3808     player->drop_delay = 0;
3809     player->drop_pressed_delay = 0;
3810
3811     player->last_jx = -1;
3812     player->last_jy = -1;
3813     player->jx = -1;
3814     player->jy = -1;
3815
3816     player->shield_normal_time_left = 0;
3817     player->shield_deadly_time_left = 0;
3818
3819     player->inventory_infinite_element = EL_UNDEFINED;
3820     player->inventory_size = 0;
3821
3822     if (level.use_initial_inventory[i])
3823     {
3824       for (j = 0; j < level.initial_inventory_size[i]; j++)
3825       {
3826         int element = level.initial_inventory_content[i][j];
3827         int collect_count = element_info[element].collect_count_initial;
3828         int k;
3829
3830         if (!IS_CUSTOM_ELEMENT(element))
3831           collect_count = 1;
3832
3833         if (collect_count == 0)
3834           player->inventory_infinite_element = element;
3835         else
3836           for (k = 0; k < collect_count; k++)
3837             if (player->inventory_size < MAX_INVENTORY_SIZE)
3838               player->inventory_element[player->inventory_size++] = element;
3839       }
3840     }
3841
3842     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843     SnapField(player, 0, 0);
3844
3845     player->LevelSolved = FALSE;
3846     player->GameOver = FALSE;
3847
3848     player->LevelSolved_GameWon = FALSE;
3849     player->LevelSolved_GameEnd = FALSE;
3850     player->LevelSolved_PanelOff = FALSE;
3851     player->LevelSolved_SaveTape = FALSE;
3852     player->LevelSolved_SaveScore = FALSE;
3853     player->LevelSolved_CountingTime = 0;
3854     player->LevelSolved_CountingScore = 0;
3855
3856     map_player_action[i] = i;
3857   }
3858
3859   network_player_action_received = FALSE;
3860
3861 #if defined(NETWORK_AVALIABLE)
3862   /* initial null action */
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865 #endif
3866
3867   ZX = ZY = -1;
3868   ExitX = ExitY = -1;
3869
3870   FrameCounter = 0;
3871   TimeFrames = 0;
3872   TimePlayed = 0;
3873   TimeLeft = level.time;
3874   TapeTime = 0;
3875
3876   ScreenMovDir = MV_NONE;
3877   ScreenMovPos = 0;
3878   ScreenGfxPos = 0;
3879
3880   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3881
3882   AllPlayersGone = FALSE;
3883
3884   game.yamyam_content_nr = 0;
3885   game.robot_wheel_active = FALSE;
3886   game.magic_wall_active = FALSE;
3887   game.magic_wall_time_left = 0;
3888   game.light_time_left = 0;
3889   game.timegate_time_left = 0;
3890   game.switchgate_pos = 0;
3891   game.wind_direction = level.wind_direction_initial;
3892
3893 #if !USE_PLAYER_GRAVITY
3894   game.gravity = FALSE;
3895   game.explosions_delayed = TRUE;
3896 #endif
3897
3898   game.lenses_time_left = 0;
3899   game.magnify_time_left = 0;
3900
3901   game.ball_state = level.ball_state_initial;
3902   game.ball_content_nr = 0;
3903
3904   game.envelope_active = FALSE;
3905
3906   /* set focus to local player for network games, else to all players */
3907   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908   game.centered_player_nr_next = game.centered_player_nr;
3909   game.set_centered_player = FALSE;
3910
3911   if (network_playing && tape.recording)
3912   {
3913     /* store client dependent player focus when recording network games */
3914     tape.centered_player_nr_next = game.centered_player_nr_next;
3915     tape.set_centered_player = TRUE;
3916   }
3917
3918   for (i = 0; i < NUM_BELTS; i++)
3919   {
3920     game.belt_dir[i] = MV_NONE;
3921     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3922   }
3923
3924   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3926
3927   SCAN_PLAYFIELD(x, y)
3928   {
3929     Feld[x][y] = level.field[x][y];
3930     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931     ChangeDelay[x][y] = 0;
3932     ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934     CustomValue[x][y] = 0;              /* initialized in InitField() */
3935 #endif
3936     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3937     AmoebaNr[x][y] = 0;
3938     WasJustMoving[x][y] = 0;
3939     WasJustFalling[x][y] = 0;
3940     CheckCollision[x][y] = 0;
3941     CheckImpact[x][y] = 0;
3942     Stop[x][y] = FALSE;
3943     Pushed[x][y] = FALSE;
3944
3945     ChangeCount[x][y] = 0;
3946     ChangeEvent[x][y] = -1;
3947
3948     ExplodePhase[x][y] = 0;
3949     ExplodeDelay[x][y] = 0;
3950     ExplodeField[x][y] = EX_TYPE_NONE;
3951
3952     RunnerVisit[x][y] = 0;
3953     PlayerVisit[x][y] = 0;
3954
3955     GfxFrame[x][y] = 0;
3956     GfxRandom[x][y] = INIT_GFX_RANDOM();
3957     GfxElement[x][y] = EL_UNDEFINED;
3958     GfxAction[x][y] = ACTION_DEFAULT;
3959     GfxDir[x][y] = MV_NONE;
3960     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3961   }
3962
3963   SCAN_PLAYFIELD(x, y)
3964   {
3965     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3966       emulate_bd = FALSE;
3967     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3968       emulate_sb = FALSE;
3969     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3970       emulate_sp = FALSE;
3971
3972     InitField(x, y, TRUE);
3973
3974     ResetGfxAnimation(x, y);
3975   }
3976
3977   InitBeltMovement();
3978
3979   for (i = 0; i < MAX_PLAYERS; i++)
3980   {
3981     struct PlayerInfo *player = &stored_player[i];
3982
3983     /* set number of special actions for bored and sleeping animation */
3984     player->num_special_action_bored =
3985       get_num_special_action(player->artwork_element,
3986                              ACTION_BORING_1, ACTION_BORING_LAST);
3987     player->num_special_action_sleeping =
3988       get_num_special_action(player->artwork_element,
3989                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3990   }
3991
3992   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993                     emulate_sb ? EMU_SOKOBAN :
3994                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3995
3996 #if USE_NEW_ALL_SLIPPERY
3997   /* initialize type of slippery elements */
3998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3999   {
4000     if (!IS_CUSTOM_ELEMENT(i))
4001     {
4002       /* default: elements slip down either to the left or right randomly */
4003       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4004
4005       /* SP style elements prefer to slip down on the left side */
4006       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008
4009       /* BD style elements prefer to slip down on the left side */
4010       if (game.emulation == EMU_BOULDERDASH)
4011         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4012     }
4013   }
4014 #endif
4015
4016   /* initialize explosion and ignition delay */
4017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018   {
4019     if (!IS_CUSTOM_ELEMENT(i))
4020     {
4021       int num_phase = 8;
4022       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025       int last_phase = (num_phase + 1) * delay;
4026       int half_phase = (num_phase / 2) * delay;
4027
4028       element_info[i].explosion_delay = last_phase - 1;
4029       element_info[i].ignition_delay = half_phase;
4030
4031       if (i == EL_BLACK_ORB)
4032         element_info[i].ignition_delay = 1;
4033     }
4034
4035 #if 0
4036     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
4037       element_info[i].explosion_delay = 1;
4038
4039     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
4040       element_info[i].ignition_delay = 1;
4041 #endif
4042   }
4043
4044   /* correct non-moving belts to start moving left */
4045   for (i = 0; i < NUM_BELTS; i++)
4046     if (game.belt_dir[i] == MV_NONE)
4047       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
4048
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051   /* choose default local player */
4052   local_player = &stored_player[0];
4053
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055     stored_player[i].connected = FALSE;
4056
4057   local_player->connected = TRUE;
4058   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4059
4060   if (tape.playing)
4061   {
4062     /* try to guess locally connected team mode players (needed for correct
4063        assignment of player figures from level to locally playing players) */
4064
4065     for (i = 0; i < MAX_PLAYERS; i++)
4066       if (tape.player_participates[i])
4067         stored_player[i].connected = TRUE;
4068   }
4069   else if (setup.team_mode && !options.network)
4070   {
4071     /* try to guess locally connected team mode players (needed for correct
4072        assignment of player figures from level to locally playing players) */
4073
4074     for (i = 0; i < MAX_PLAYERS; i++)
4075       if (setup.input[i].use_joystick ||
4076           setup.input[i].key.left != KSYM_UNDEFINED)
4077         stored_player[i].connected = TRUE;
4078   }
4079
4080 #if 0
4081   for (i = 0; i < MAX_PLAYERS; i++)
4082     printf("::: player %d: %s\n", i,
4083            (stored_player[i].connected ? "connected" : "not connected"));
4084
4085   for (i = 0; i < MAX_PLAYERS; i++)
4086     printf("::: player %d: %s\n", i,
4087            (stored_player[i].present ? "present" : "not present"));
4088 #endif
4089
4090   /* check if any connected player was not found in playfield */
4091   for (i = 0; i < MAX_PLAYERS; i++)
4092   {
4093     struct PlayerInfo *player = &stored_player[i];
4094
4095     if (player->connected && !player->present)
4096     {
4097       struct PlayerInfo *field_player = NULL;
4098
4099 #if 0
4100       printf("::: looking for field player for player %d ...\n", i);
4101 #endif
4102
4103       /* assign first free player found that is present in the playfield */
4104
4105       /* first try: look for unmapped playfield player that is not connected */
4106       if (field_player == NULL)
4107         for (j = 0; j < MAX_PLAYERS; j++)
4108           if (stored_player[j].present &&
4109               !stored_player[j].mapped &&
4110               !stored_player[j].connected)
4111             field_player = &stored_player[j];
4112
4113       /* second try: look for *any* unmapped playfield player */
4114       if (field_player == NULL)
4115         for (j = 0; j < MAX_PLAYERS; j++)
4116           if (stored_player[j].present &&
4117               !stored_player[j].mapped)
4118             field_player = &stored_player[j];
4119
4120       if (field_player != NULL)
4121       {
4122         int jx = field_player->jx, jy = field_player->jy;
4123
4124 #if 0
4125         printf("::: found player figure %d\n", field_player->index_nr);
4126 #endif
4127
4128         player->present = FALSE;
4129         player->active = FALSE;
4130
4131         field_player->present = TRUE;
4132         field_player->active = TRUE;
4133
4134         /*
4135         player->initial_element = field_player->initial_element;
4136         player->artwork_element = field_player->artwork_element;
4137
4138         player->block_last_field       = field_player->block_last_field;
4139         player->block_delay_adjustment = field_player->block_delay_adjustment;
4140         */
4141
4142         StorePlayer[jx][jy] = field_player->element_nr;
4143
4144         field_player->jx = field_player->last_jx = jx;
4145         field_player->jy = field_player->last_jy = jy;
4146
4147         if (local_player == player)
4148           local_player = field_player;
4149
4150         map_player_action[field_player->index_nr] = i;
4151
4152         field_player->mapped = TRUE;
4153
4154 #if 0
4155         printf("::: map_player_action[%d] == %d\n",
4156                field_player->index_nr, i);
4157 #endif
4158       }
4159     }
4160
4161     if (player->connected && player->present)
4162       player->mapped = TRUE;
4163   }
4164
4165 #else
4166
4167   /* check if any connected player was not found in playfield */
4168   for (i = 0; i < MAX_PLAYERS; i++)
4169   {
4170     struct PlayerInfo *player = &stored_player[i];
4171
4172     if (player->connected && !player->present)
4173     {
4174       for (j = 0; j < MAX_PLAYERS; j++)
4175       {
4176         struct PlayerInfo *field_player = &stored_player[j];
4177         int jx = field_player->jx, jy = field_player->jy;
4178
4179         /* assign first free player found that is present in the playfield */
4180         if (field_player->present && !field_player->connected)
4181         {
4182           player->present = TRUE;
4183           player->active = TRUE;
4184
4185           field_player->present = FALSE;
4186           field_player->active = FALSE;
4187
4188           player->initial_element = field_player->initial_element;
4189           player->artwork_element = field_player->artwork_element;
4190
4191           player->block_last_field       = field_player->block_last_field;
4192           player->block_delay_adjustment = field_player->block_delay_adjustment;
4193
4194           StorePlayer[jx][jy] = player->element_nr;
4195
4196           player->jx = player->last_jx = jx;
4197           player->jy = player->last_jy = jy;
4198
4199           break;
4200         }
4201       }
4202     }
4203   }
4204 #endif
4205
4206 #if 0
4207   printf("::: local_player->present == %d\n", local_player->present);
4208 #endif
4209
4210   if (tape.playing)
4211   {
4212     /* when playing a tape, eliminate all players who do not participate */
4213
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215     for (i = 0; i < MAX_PLAYERS; i++)
4216     {
4217       if (stored_player[i].active &&
4218           !tape.player_participates[map_player_action[i]])
4219       {
4220         struct PlayerInfo *player = &stored_player[i];
4221         int jx = player->jx, jy = player->jy;
4222
4223         player->active = FALSE;
4224         StorePlayer[jx][jy] = 0;
4225         Feld[jx][jy] = EL_EMPTY;
4226       }
4227     }
4228 #else
4229     for (i = 0; i < MAX_PLAYERS; i++)
4230     {
4231       if (stored_player[i].active &&
4232           !tape.player_participates[i])
4233       {
4234         struct PlayerInfo *player = &stored_player[i];
4235         int jx = player->jx, jy = player->jy;
4236
4237         player->active = FALSE;
4238         StorePlayer[jx][jy] = 0;
4239         Feld[jx][jy] = EL_EMPTY;
4240       }
4241     }
4242 #endif
4243   }
4244   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
4245   {
4246     /* when in single player mode, eliminate all but the first active player */
4247
4248     for (i = 0; i < MAX_PLAYERS; i++)
4249     {
4250       if (stored_player[i].active)
4251       {
4252         for (j = i + 1; j < MAX_PLAYERS; j++)
4253         {
4254           if (stored_player[j].active)
4255           {
4256             struct PlayerInfo *player = &stored_player[j];
4257             int jx = player->jx, jy = player->jy;
4258
4259             player->active = FALSE;
4260             player->present = FALSE;
4261
4262             StorePlayer[jx][jy] = 0;
4263             Feld[jx][jy] = EL_EMPTY;
4264           }
4265         }
4266       }
4267     }
4268   }
4269
4270   /* when recording the game, store which players take part in the game */
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284   if (options.debug)
4285   {
4286     for (i = 0; i < MAX_PLAYERS; i++)
4287     {
4288       struct PlayerInfo *player = &stored_player[i];
4289
4290       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4291              i+1,
4292              player->present,
4293              player->connected,
4294              player->active);
4295       if (local_player == player)
4296         printf("Player  %d is local player.\n", i+1);
4297     }
4298   }
4299
4300   if (BorderElement == EL_EMPTY)
4301   {
4302     SBX_Left = 0;
4303     SBX_Right = lev_fieldx - SCR_FIELDX;
4304     SBY_Upper = 0;
4305     SBY_Lower = lev_fieldy - SCR_FIELDY;
4306   }
4307   else
4308   {
4309     SBX_Left = -1;
4310     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4311     SBY_Upper = -1;
4312     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4313   }
4314
4315   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4317
4318   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4320
4321   /* if local player not found, look for custom element that might create
4322      the player (make some assumptions about the right custom element) */
4323   if (!local_player->present)
4324   {
4325     int start_x = 0, start_y = 0;
4326     int found_rating = 0;
4327     int found_element = EL_UNDEFINED;
4328     int player_nr = local_player->index_nr;
4329
4330     SCAN_PLAYFIELD(x, y)
4331     {
4332       int element = Feld[x][y];
4333       int content;
4334       int xx, yy;
4335       boolean is_player;
4336
4337       if (level.use_start_element[player_nr] &&
4338           level.start_element[player_nr] == element &&
4339           found_rating < 4)
4340       {
4341         start_x = x;
4342         start_y = y;
4343
4344         found_rating = 4;
4345         found_element = element;
4346       }
4347
4348       if (!IS_CUSTOM_ELEMENT(element))
4349         continue;
4350
4351       if (CAN_CHANGE(element))
4352       {
4353         for (i = 0; i < element_info[element].num_change_pages; i++)
4354         {
4355           /* check for player created from custom element as single target */
4356           content = element_info[element].change_page[i].target_element;
4357           is_player = ELEM_IS_PLAYER(content);
4358
4359           if (is_player && (found_rating < 3 ||
4360                             (found_rating == 3 && element < found_element)))
4361           {
4362             start_x = x;
4363             start_y = y;
4364
4365             found_rating = 3;
4366             found_element = element;
4367           }
4368         }
4369       }
4370
4371       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4372       {
4373         /* check for player created from custom element as explosion content */
4374         content = element_info[element].content.e[xx][yy];
4375         is_player = ELEM_IS_PLAYER(content);
4376
4377         if (is_player && (found_rating < 2 ||
4378                           (found_rating == 2 && element < found_element)))
4379         {
4380           start_x = x + xx - 1;
4381           start_y = y + yy - 1;
4382
4383           found_rating = 2;
4384           found_element = element;
4385         }
4386
4387         if (!CAN_CHANGE(element))
4388           continue;
4389
4390         for (i = 0; i < element_info[element].num_change_pages; i++)
4391         {
4392           /* check for player created from custom element as extended target */
4393           content =
4394             element_info[element].change_page[i].target_content.e[xx][yy];
4395
4396           is_player = ELEM_IS_PLAYER(content);
4397
4398           if (is_player && (found_rating < 1 ||
4399                             (found_rating == 1 && element < found_element)))
4400           {
4401             start_x = x + xx - 1;
4402             start_y = y + yy - 1;
4403
4404             found_rating = 1;
4405             found_element = element;
4406           }
4407         }
4408       }
4409     }
4410
4411     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4412                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4413                 start_x - MIDPOSX);
4414
4415     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4417                 start_y - MIDPOSY);
4418   }
4419   else
4420   {
4421     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4422                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423                 local_player->jx - MIDPOSX);
4424
4425     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427                 local_player->jy - MIDPOSY);
4428   }
4429
4430 #if 0
4431   /* do not use PLAYING mask for fading out from main screen */
4432   game_status = GAME_MODE_MAIN;
4433 #endif
4434
4435   StopAnimation();
4436
4437   if (!game.restart_level)
4438     CloseDoor(DOOR_CLOSE_1);
4439
4440 #if 1
4441   if (level_editor_test_game)
4442     FadeSkipNextFadeIn();
4443   else
4444     FadeSetEnterScreen();
4445 #else
4446   if (level_editor_test_game)
4447     fading = fading_none;
4448   else
4449     fading = menu.destination;
4450 #endif
4451
4452 #if 1
4453   FadeOut(REDRAW_FIELD);
4454 #else
4455   if (do_fading)
4456     FadeOut(REDRAW_FIELD);
4457 #endif
4458
4459 #if 0
4460   game_status = GAME_MODE_PLAYING;
4461 #endif
4462
4463   /* !!! FIX THIS (START) !!! */
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4465   {
4466     InitGameEngine_EM();
4467
4468     /* blit playfield from scroll buffer to normal back buffer for fading in */
4469     BlitScreenToBitmap_EM(backbuffer);
4470   }
4471   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4472   {
4473     InitGameEngine_SP();
4474
4475     /* blit playfield from scroll buffer to normal back buffer for fading in */
4476     BlitScreenToBitmap_SP(backbuffer);
4477   }
4478   else
4479   {
4480     DrawLevel();
4481     DrawAllPlayers();
4482
4483     /* after drawing the level, correct some elements */
4484     if (game.timegate_time_left == 0)
4485       CloseAllOpenTimegates();
4486
4487     /* blit playfield from scroll buffer to normal back buffer for fading in */
4488     if (setup.soft_scrolling)
4489       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4490
4491     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4492   }
4493   /* !!! FIX THIS (END) !!! */
4494
4495 #if 1
4496   FadeIn(REDRAW_FIELD);
4497 #else
4498   if (do_fading)
4499     FadeIn(REDRAW_FIELD);
4500
4501   BackToFront();
4502 #endif
4503
4504   if (!game.restart_level)
4505   {
4506     /* copy default game door content to main double buffer */
4507     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4508                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4509   }
4510
4511   SetPanelBackground();
4512   SetDrawBackgroundMask(REDRAW_DOOR_1);
4513
4514 #if 1
4515   UpdateAndDisplayGameControlValues();
4516 #else
4517   UpdateGameDoorValues();
4518   DrawGameDoorValues();
4519 #endif
4520
4521   if (!game.restart_level)
4522   {
4523     UnmapGameButtons();
4524     UnmapTapeButtons();
4525     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4526     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4527     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4528     MapGameButtons();
4529     MapTapeButtons();
4530
4531     /* copy actual game door content to door double buffer for OpenDoor() */
4532     BlitBitmap(drawto, bitmap_db_door,
4533                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4534
4535     OpenDoor(DOOR_OPEN_ALL);
4536
4537     PlaySound(SND_GAME_STARTING);
4538
4539     if (setup.sound_music)
4540       PlayLevelMusic();
4541
4542     KeyboardAutoRepeatOffUnlessAutoplay();
4543
4544     if (options.debug)
4545     {
4546       for (i = 0; i < MAX_PLAYERS; i++)
4547         printf("Player %d %sactive.\n",
4548                i + 1, (stored_player[i].active ? "" : "not "));
4549     }
4550   }
4551
4552 #if 1
4553   UnmapAllGadgets();
4554
4555   MapGameButtons();
4556   MapTapeButtons();
4557 #endif
4558
4559   game.restart_level = FALSE;
4560 }
4561
4562 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4563 {
4564   /* this is used for non-R'n'D game engines to update certain engine values */
4565
4566   /* needed to determine if sounds are played within the visible screen area */
4567   scroll_x = actual_scroll_x;
4568   scroll_y = actual_scroll_y;
4569 }
4570
4571 void InitMovDir(int x, int y)
4572 {
4573   int i, element = Feld[x][y];
4574   static int xy[4][2] =
4575   {
4576     {  0, +1 },
4577     { +1,  0 },
4578     {  0, -1 },
4579     { -1,  0 }
4580   };
4581   static int direction[3][4] =
4582   {
4583     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4584     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4585     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4586   };
4587
4588   switch (element)
4589   {
4590     case EL_BUG_RIGHT:
4591     case EL_BUG_UP:
4592     case EL_BUG_LEFT:
4593     case EL_BUG_DOWN:
4594       Feld[x][y] = EL_BUG;
4595       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4596       break;
4597
4598     case EL_SPACESHIP_RIGHT:
4599     case EL_SPACESHIP_UP:
4600     case EL_SPACESHIP_LEFT:
4601     case EL_SPACESHIP_DOWN:
4602       Feld[x][y] = EL_SPACESHIP;
4603       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4604       break;
4605
4606     case EL_BD_BUTTERFLY_RIGHT:
4607     case EL_BD_BUTTERFLY_UP:
4608     case EL_BD_BUTTERFLY_LEFT:
4609     case EL_BD_BUTTERFLY_DOWN:
4610       Feld[x][y] = EL_BD_BUTTERFLY;
4611       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4612       break;
4613
4614     case EL_BD_FIREFLY_RIGHT:
4615     case EL_BD_FIREFLY_UP:
4616     case EL_BD_FIREFLY_LEFT:
4617     case EL_BD_FIREFLY_DOWN:
4618       Feld[x][y] = EL_BD_FIREFLY;
4619       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4620       break;
4621
4622     case EL_PACMAN_RIGHT:
4623     case EL_PACMAN_UP:
4624     case EL_PACMAN_LEFT:
4625     case EL_PACMAN_DOWN:
4626       Feld[x][y] = EL_PACMAN;
4627       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4628       break;
4629
4630     case EL_YAMYAM_LEFT:
4631     case EL_YAMYAM_RIGHT:
4632     case EL_YAMYAM_UP:
4633     case EL_YAMYAM_DOWN:
4634       Feld[x][y] = EL_YAMYAM;
4635       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4636       break;
4637
4638     case EL_SP_SNIKSNAK:
4639       MovDir[x][y] = MV_UP;
4640       break;
4641
4642     case EL_SP_ELECTRON:
4643       MovDir[x][y] = MV_LEFT;
4644       break;
4645
4646     case EL_MOLE_LEFT:
4647     case EL_MOLE_RIGHT:
4648     case EL_MOLE_UP:
4649     case EL_MOLE_DOWN:
4650       Feld[x][y] = EL_MOLE;
4651       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4652       break;
4653
4654     default:
4655       if (IS_CUSTOM_ELEMENT(element))
4656       {
4657         struct ElementInfo *ei = &element_info[element];
4658         int move_direction_initial = ei->move_direction_initial;
4659         int move_pattern = ei->move_pattern;
4660
4661         if (move_direction_initial == MV_START_PREVIOUS)
4662         {
4663           if (MovDir[x][y] != MV_NONE)
4664             return;
4665
4666           move_direction_initial = MV_START_AUTOMATIC;
4667         }
4668
4669         if (move_direction_initial == MV_START_RANDOM)
4670           MovDir[x][y] = 1 << RND(4);
4671         else if (move_direction_initial & MV_ANY_DIRECTION)
4672           MovDir[x][y] = move_direction_initial;
4673         else if (move_pattern == MV_ALL_DIRECTIONS ||
4674                  move_pattern == MV_TURNING_LEFT ||
4675                  move_pattern == MV_TURNING_RIGHT ||
4676                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4677                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4678                  move_pattern == MV_TURNING_RANDOM)
4679           MovDir[x][y] = 1 << RND(4);
4680         else if (move_pattern == MV_HORIZONTAL)
4681           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4682         else if (move_pattern == MV_VERTICAL)
4683           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4684         else if (move_pattern & MV_ANY_DIRECTION)
4685           MovDir[x][y] = element_info[element].move_pattern;
4686         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4687                  move_pattern == MV_ALONG_RIGHT_SIDE)
4688         {
4689           /* use random direction as default start direction */
4690           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4691             MovDir[x][y] = 1 << RND(4);
4692
4693           for (i = 0; i < NUM_DIRECTIONS; i++)
4694           {
4695             int x1 = x + xy[i][0];
4696             int y1 = y + xy[i][1];
4697
4698             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4699             {
4700               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4701                 MovDir[x][y] = direction[0][i];
4702               else
4703                 MovDir[x][y] = direction[1][i];
4704
4705               break;
4706             }
4707           }
4708         }                
4709       }
4710       else
4711       {
4712         MovDir[x][y] = 1 << RND(4);
4713
4714         if (element != EL_BUG &&
4715             element != EL_SPACESHIP &&
4716             element != EL_BD_BUTTERFLY &&
4717             element != EL_BD_FIREFLY)
4718           break;
4719
4720         for (i = 0; i < NUM_DIRECTIONS; i++)
4721         {
4722           int x1 = x + xy[i][0];
4723           int y1 = y + xy[i][1];
4724
4725           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4726           {
4727             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4728             {
4729               MovDir[x][y] = direction[0][i];
4730               break;
4731             }
4732             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4734             {
4735               MovDir[x][y] = direction[1][i];
4736               break;
4737             }
4738           }
4739         }
4740       }
4741       break;
4742   }
4743
4744   GfxDir[x][y] = MovDir[x][y];
4745 }
4746
4747 void InitAmoebaNr(int x, int y)
4748 {
4749   int i;
4750   int group_nr = AmoebeNachbarNr(x, y);
4751
4752   if (group_nr == 0)
4753   {
4754     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4755     {
4756       if (AmoebaCnt[i] == 0)
4757       {
4758         group_nr = i;
4759         break;
4760       }
4761     }
4762   }
4763
4764   AmoebaNr[x][y] = group_nr;
4765   AmoebaCnt[group_nr]++;
4766   AmoebaCnt2[group_nr]++;
4767 }
4768
4769 static void PlayerWins(struct PlayerInfo *player)
4770 {
4771   player->LevelSolved = TRUE;
4772   player->GameOver = TRUE;
4773
4774   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4775                          level.native_em_level->lev->score : player->score);
4776
4777   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4778   player->LevelSolved_CountingScore = player->score_final;
4779 }
4780
4781 void GameWon()
4782 {
4783   static int time, time_final;
4784   static int score, score_final;
4785   static int game_over_delay_1 = 0;
4786   static int game_over_delay_2 = 0;
4787   int game_over_delay_value_1 = 50;
4788   int game_over_delay_value_2 = 50;
4789
4790   if (!local_player->LevelSolved_GameWon)
4791   {
4792     int i;
4793
4794     /* do not start end game actions before the player stops moving (to exit) */
4795     if (local_player->MovPos)
4796       return;
4797
4798     local_player->LevelSolved_GameWon = TRUE;
4799     local_player->LevelSolved_SaveTape = tape.recording;
4800     local_player->LevelSolved_SaveScore = !tape.playing;
4801
4802     if (tape.auto_play)         /* tape might already be stopped here */
4803       tape.auto_play_level_solved = TRUE;
4804
4805 #if 1
4806     TapeStop();
4807 #endif
4808
4809     game_over_delay_1 = game_over_delay_value_1;
4810     game_over_delay_2 = game_over_delay_value_2;
4811
4812     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4813     score = score_final = local_player->score_final;
4814
4815     if (TimeLeft > 0)
4816     {
4817       time_final = 0;
4818       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4819     }
4820     else if (level.time == 0 && TimePlayed < 999)
4821     {
4822       time_final = 999;
4823       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4824     }
4825
4826     local_player->score_final = score_final;
4827
4828     if (level_editor_test_game)
4829     {
4830       time = time_final;
4831       score = score_final;
4832
4833 #if 1
4834       local_player->LevelSolved_CountingTime = time;
4835       local_player->LevelSolved_CountingScore = score;
4836
4837       game_panel_controls[GAME_PANEL_TIME].value = time;
4838       game_panel_controls[GAME_PANEL_SCORE].value = score;
4839
4840       DisplayGameControlValues();
4841 #else
4842       DrawGameValue_Time(time);
4843       DrawGameValue_Score(score);
4844 #endif
4845     }
4846
4847     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4848     {
4849       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4850       {
4851         /* close exit door after last player */
4852         if ((AllPlayersGone &&
4853              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4854               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4855               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4856             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4857             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4858         {
4859           int element = Feld[ExitX][ExitY];
4860
4861 #if 0
4862           if (element == EL_EM_EXIT_OPEN ||
4863               element == EL_EM_STEEL_EXIT_OPEN)
4864           {
4865             Bang(ExitX, ExitY);
4866           }
4867           else
4868 #endif
4869           {
4870             Feld[ExitX][ExitY] =
4871               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4872                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4873                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4874                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4875                EL_EM_STEEL_EXIT_CLOSING);
4876
4877             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4878           }
4879         }
4880
4881         /* player disappears */
4882         DrawLevelField(ExitX, ExitY);
4883       }
4884
4885       for (i = 0; i < MAX_PLAYERS; i++)
4886       {
4887         struct PlayerInfo *player = &stored_player[i];
4888
4889         if (player->present)
4890         {
4891           RemovePlayer(player);
4892
4893           /* player disappears */
4894           DrawLevelField(player->jx, player->jy);
4895         }
4896       }
4897     }
4898
4899     PlaySound(SND_GAME_WINNING);
4900   }
4901
4902   if (game_over_delay_1 > 0)
4903   {
4904     game_over_delay_1--;
4905
4906     return;
4907   }
4908
4909   if (time != time_final)
4910   {
4911     int time_to_go = ABS(time_final - time);
4912     int time_count_dir = (time < time_final ? +1 : -1);
4913     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4914
4915     time  += time_count_steps * time_count_dir;
4916     score += time_count_steps * level.score[SC_TIME_BONUS];
4917
4918 #if 1
4919     local_player->LevelSolved_CountingTime = time;
4920     local_player->LevelSolved_CountingScore = score;
4921
4922     game_panel_controls[GAME_PANEL_TIME].value = time;
4923     game_panel_controls[GAME_PANEL_SCORE].value = score;
4924
4925     DisplayGameControlValues();
4926 #else
4927     DrawGameValue_Time(time);
4928     DrawGameValue_Score(score);
4929 #endif
4930
4931     if (time == time_final)
4932       StopSound(SND_GAME_LEVELTIME_BONUS);
4933     else if (setup.sound_loops)
4934       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4935     else
4936       PlaySound(SND_GAME_LEVELTIME_BONUS);
4937
4938     return;
4939   }
4940
4941   local_player->LevelSolved_PanelOff = TRUE;
4942
4943   if (game_over_delay_2 > 0)
4944   {
4945     game_over_delay_2--;
4946
4947     return;
4948   }
4949
4950 #if 1
4951   GameEnd();
4952 #endif
4953 }
4954
4955 void GameEnd()
4956 {
4957   int hi_pos;
4958   boolean raise_level = FALSE;
4959
4960   local_player->LevelSolved_GameEnd = TRUE;
4961
4962   CloseDoor(DOOR_CLOSE_1);
4963
4964   if (local_player->LevelSolved_SaveTape)
4965   {
4966 #if 0
4967     TapeStop();
4968 #endif
4969
4970 #if 1
4971     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4972 #else
4973     SaveTape(tape.level_nr);            /* ask to save tape */
4974 #endif
4975   }
4976
4977   if (level_editor_test_game)
4978   {
4979     game_status = GAME_MODE_MAIN;
4980
4981 #if 1
4982     DrawAndFadeInMainMenu(REDRAW_FIELD);
4983 #else
4984     DrawMainMenu();
4985 #endif
4986
4987     return;
4988   }
4989
4990   if (!local_player->LevelSolved_SaveScore)
4991   {
4992 #if 1
4993     FadeOut(REDRAW_FIELD);
4994 #endif
4995
4996     game_status = GAME_MODE_MAIN;
4997
4998     DrawAndFadeInMainMenu(REDRAW_FIELD);
4999
5000     return;
5001   }
5002
5003   if (level_nr == leveldir_current->handicap_level)
5004   {
5005     leveldir_current->handicap_level++;
5006     SaveLevelSetup_SeriesInfo();
5007   }
5008
5009   if (level_nr < leveldir_current->last_level)
5010     raise_level = TRUE;                 /* advance to next level */
5011
5012   if ((hi_pos = NewHiScore()) >= 0) 
5013   {
5014     game_status = GAME_MODE_SCORES;
5015
5016     DrawHallOfFame(hi_pos);
5017
5018     if (raise_level)
5019     {
5020       level_nr++;
5021       TapeErase();
5022     }
5023   }
5024   else
5025   {
5026 #if 1
5027     FadeOut(REDRAW_FIELD);
5028 #endif
5029
5030     game_status = GAME_MODE_MAIN;
5031
5032     if (raise_level)
5033     {
5034       level_nr++;
5035       TapeErase();
5036     }
5037
5038     DrawAndFadeInMainMenu(REDRAW_FIELD);
5039   }
5040 }
5041
5042 int NewHiScore()
5043 {
5044   int k, l;
5045   int position = -1;
5046
5047   LoadScore(level_nr);
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
5051     return -1;
5052
5053   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
5054   {
5055     if (local_player->score_final > highscore[k].Score)
5056     {
5057       /* player has made it to the hall of fame */
5058
5059       if (k < MAX_SCORE_ENTRIES - 1)
5060       {
5061         int m = MAX_SCORE_ENTRIES - 1;
5062
5063 #ifdef ONE_PER_NAME
5064         for (l = k; l < MAX_SCORE_ENTRIES; l++)
5065           if (strEqual(setup.player_name, highscore[l].Name))
5066             m = l;
5067         if (m == k)     /* player's new highscore overwrites his old one */
5068           goto put_into_list;
5069 #endif
5070
5071         for (l = m; l > k; l--)
5072         {
5073           strcpy(highscore[l].Name, highscore[l - 1].Name);
5074           highscore[l].Score = highscore[l - 1].Score;
5075         }
5076       }
5077
5078 #ifdef ONE_PER_NAME
5079       put_into_list:
5080 #endif
5081       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5082       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5083       highscore[k].Score = local_player->score_final; 
5084       position = k;
5085       break;
5086     }
5087
5088 #ifdef ONE_PER_NAME
5089     else if (!strncmp(setup.player_name, highscore[k].Name,
5090                       MAX_PLAYER_NAME_LEN))
5091       break;    /* player already there with a higher score */
5092 #endif
5093
5094   }
5095
5096   if (position >= 0) 
5097     SaveScore(level_nr);
5098
5099   return position;
5100 }
5101
5102 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5103 {
5104   int element = Feld[x][y];
5105   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5106   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5107   int horiz_move = (dx != 0);
5108   int sign = (horiz_move ? dx : dy);
5109   int step = sign * element_info[element].move_stepsize;
5110
5111   /* special values for move stepsize for spring and things on conveyor belt */
5112   if (horiz_move)
5113   {
5114     if (CAN_FALL(element) &&
5115         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5116       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5117     else if (element == EL_SPRING)
5118       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5119   }
5120
5121   return step;
5122 }
5123
5124 inline static int getElementMoveStepsize(int x, int y)
5125 {
5126   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5127 }
5128
5129 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5130 {
5131   if (player->GfxAction != action || player->GfxDir != dir)
5132   {
5133 #if 0
5134     printf("Player frame reset! (%d => %d, %d => %d)\n",
5135            player->GfxAction, action, player->GfxDir, dir);
5136 #endif
5137
5138     player->GfxAction = action;
5139     player->GfxDir = dir;
5140     player->Frame = 0;
5141     player->StepFrame = 0;
5142   }
5143 }
5144
5145 #if USE_GFX_RESET_GFX_ANIMATION
5146 static void ResetGfxFrame(int x, int y, boolean redraw)
5147 {
5148   int element = Feld[x][y];
5149   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5150   int last_gfx_frame = GfxFrame[x][y];
5151
5152   if (graphic_info[graphic].anim_global_sync)
5153     GfxFrame[x][y] = FrameCounter;
5154   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5155     GfxFrame[x][y] = CustomValue[x][y];
5156   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5157     GfxFrame[x][y] = element_info[element].collect_score;
5158   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5159     GfxFrame[x][y] = ChangeDelay[x][y];
5160
5161   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5162     DrawLevelGraphicAnimation(x, y, graphic);
5163 }
5164 #endif
5165
5166 static void ResetGfxAnimation(int x, int y)
5167 {
5168   GfxAction[x][y] = ACTION_DEFAULT;
5169   GfxDir[x][y] = MovDir[x][y];
5170   GfxFrame[x][y] = 0;
5171
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173   ResetGfxFrame(x, y, FALSE);
5174 #endif
5175 }
5176
5177 static void ResetRandomAnimationValue(int x, int y)
5178 {
5179   GfxRandom[x][y] = INIT_GFX_RANDOM();
5180 }
5181
5182 void InitMovingField(int x, int y, int direction)
5183 {
5184   int element = Feld[x][y];
5185   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5187   int newx = x + dx;
5188   int newy = y + dy;
5189   boolean is_moving_before, is_moving_after;
5190 #if 0
5191   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5192 #endif
5193
5194   /* check if element was/is moving or being moved before/after mode change */
5195 #if 1
5196 #if 1
5197   is_moving_before = (WasJustMoving[x][y] != 0);
5198 #else
5199   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5200   is_moving_before = WasJustMoving[x][y];
5201 #endif
5202 #else
5203   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5204 #endif
5205   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5206
5207   /* reset animation only for moving elements which change direction of moving
5208      or which just started or stopped moving
5209      (else CEs with property "can move" / "not moving" are reset each frame) */
5210 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5211 #if 1
5212   if (is_moving_before != is_moving_after ||
5213       direction != MovDir[x][y])
5214     ResetGfxAnimation(x, y);
5215 #else
5216   if ((is_moving_before || is_moving_after) && !continues_moving)
5217     ResetGfxAnimation(x, y);
5218 #endif
5219 #else
5220   if (!continues_moving)
5221     ResetGfxAnimation(x, y);
5222 #endif
5223
5224   MovDir[x][y] = direction;
5225   GfxDir[x][y] = direction;
5226
5227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5228   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5229                      direction == MV_DOWN && CAN_FALL(element) ?
5230                      ACTION_FALLING : ACTION_MOVING);
5231 #else
5232   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5233                      ACTION_FALLING : ACTION_MOVING);
5234 #endif
5235
5236   /* this is needed for CEs with property "can move" / "not moving" */
5237
5238   if (is_moving_after)
5239   {
5240     if (Feld[newx][newy] == EL_EMPTY)
5241       Feld[newx][newy] = EL_BLOCKED;
5242
5243     MovDir[newx][newy] = MovDir[x][y];
5244
5245 #if USE_NEW_CUSTOM_VALUE
5246     CustomValue[newx][newy] = CustomValue[x][y];
5247 #endif
5248
5249     GfxFrame[newx][newy] = GfxFrame[x][y];
5250     GfxRandom[newx][newy] = GfxRandom[x][y];
5251     GfxAction[newx][newy] = GfxAction[x][y];
5252     GfxDir[newx][newy] = GfxDir[x][y];
5253   }
5254 }
5255
5256 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5257 {
5258   int direction = MovDir[x][y];
5259   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5260   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5261
5262   *goes_to_x = newx;
5263   *goes_to_y = newy;
5264 }
5265
5266 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5267 {
5268   int oldx = x, oldy = y;
5269   int direction = MovDir[x][y];
5270
5271   if (direction == MV_LEFT)
5272     oldx++;
5273   else if (direction == MV_RIGHT)
5274     oldx--;
5275   else if (direction == MV_UP)
5276     oldy++;
5277   else if (direction == MV_DOWN)
5278     oldy--;
5279
5280   *comes_from_x = oldx;
5281   *comes_from_y = oldy;
5282 }
5283
5284 int MovingOrBlocked2Element(int x, int y)
5285 {
5286   int element = Feld[x][y];
5287
5288   if (element == EL_BLOCKED)
5289   {
5290     int oldx, oldy;
5291
5292     Blocked2Moving(x, y, &oldx, &oldy);
5293     return Feld[oldx][oldy];
5294   }
5295   else
5296     return element;
5297 }
5298
5299 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5300 {
5301   /* like MovingOrBlocked2Element(), but if element is moving
5302      and (x,y) is the field the moving element is just leaving,
5303      return EL_BLOCKED instead of the element value */
5304   int element = Feld[x][y];
5305
5306   if (IS_MOVING(x, y))
5307   {
5308     if (element == EL_BLOCKED)
5309     {
5310       int oldx, oldy;
5311
5312       Blocked2Moving(x, y, &oldx, &oldy);
5313       return Feld[oldx][oldy];
5314     }
5315     else
5316       return EL_BLOCKED;
5317   }
5318   else
5319     return element;
5320 }
5321
5322 static void RemoveField(int x, int y)
5323 {
5324   Feld[x][y] = EL_EMPTY;
5325
5326   MovPos[x][y] = 0;
5327   MovDir[x][y] = 0;
5328   MovDelay[x][y] = 0;
5329
5330 #if USE_NEW_CUSTOM_VALUE
5331   CustomValue[x][y] = 0;
5332 #endif
5333
5334   AmoebaNr[x][y] = 0;
5335   ChangeDelay[x][y] = 0;
5336   ChangePage[x][y] = -1;
5337   Pushed[x][y] = FALSE;
5338
5339 #if 0
5340   ExplodeField[x][y] = EX_TYPE_NONE;
5341 #endif
5342
5343   GfxElement[x][y] = EL_UNDEFINED;
5344   GfxAction[x][y] = ACTION_DEFAULT;
5345   GfxDir[x][y] = MV_NONE;
5346 #if 0
5347   /* !!! this would prevent the removed tile from being redrawn !!! */
5348   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5349 #endif
5350 }
5351
5352 void RemoveMovingField(int x, int y)
5353 {
5354   int oldx = x, oldy = y, newx = x, newy = y;
5355   int element = Feld[x][y];
5356   int next_element = EL_UNDEFINED;
5357
5358   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5359     return;
5360
5361   if (IS_MOVING(x, y))
5362   {
5363     Moving2Blocked(x, y, &newx, &newy);
5364
5365     if (Feld[newx][newy] != EL_BLOCKED)
5366     {
5367       /* element is moving, but target field is not free (blocked), but
5368          already occupied by something different (example: acid pool);
5369          in this case, only remove the moving field, but not the target */
5370
5371       RemoveField(oldx, oldy);
5372
5373       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5374
5375       TEST_DrawLevelField(oldx, oldy);
5376
5377       return;
5378     }
5379   }
5380   else if (element == EL_BLOCKED)
5381   {
5382     Blocked2Moving(x, y, &oldx, &oldy);
5383     if (!IS_MOVING(oldx, oldy))
5384       return;
5385   }
5386
5387   if (element == EL_BLOCKED &&
5388       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5389        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5390        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5391        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5392        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5393        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5394     next_element = get_next_element(Feld[oldx][oldy]);
5395
5396   RemoveField(oldx, oldy);
5397   RemoveField(newx, newy);
5398
5399   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5400
5401   if (next_element != EL_UNDEFINED)
5402     Feld[oldx][oldy] = next_element;
5403
5404   TEST_DrawLevelField(oldx, oldy);
5405   TEST_DrawLevelField(newx, newy);
5406 }
5407
5408 void DrawDynamite(int x, int y)
5409 {
5410   int sx = SCREENX(x), sy = SCREENY(y);
5411   int graphic = el2img(Feld[x][y]);
5412   int frame;
5413
5414   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5415     return;
5416
5417   if (IS_WALKABLE_INSIDE(Back[x][y]))
5418     return;
5419
5420   if (Back[x][y])
5421     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5422   else if (Store[x][y])
5423     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5424
5425   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5426
5427   if (Back[x][y] || Store[x][y])
5428     DrawGraphicThruMask(sx, sy, graphic, frame);
5429   else
5430     DrawGraphic(sx, sy, graphic, frame);
5431 }
5432
5433 void CheckDynamite(int x, int y)
5434 {
5435   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5436   {
5437     MovDelay[x][y]--;
5438
5439     if (MovDelay[x][y] != 0)
5440     {
5441       DrawDynamite(x, y);
5442       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5443
5444       return;
5445     }
5446   }
5447
5448   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5449
5450   Bang(x, y);
5451 }
5452
5453 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5454 {
5455   boolean num_checked_players = 0;
5456   int i;
5457
5458   for (i = 0; i < MAX_PLAYERS; i++)
5459   {
5460     if (stored_player[i].active)
5461     {
5462       int sx = stored_player[i].jx;
5463       int sy = stored_player[i].jy;
5464
5465       if (num_checked_players == 0)
5466       {
5467         *sx1 = *sx2 = sx;
5468         *sy1 = *sy2 = sy;
5469       }
5470       else
5471       {
5472         *sx1 = MIN(*sx1, sx);
5473         *sy1 = MIN(*sy1, sy);
5474         *sx2 = MAX(*sx2, sx);
5475         *sy2 = MAX(*sy2, sy);
5476       }
5477
5478       num_checked_players++;
5479     }
5480   }
5481 }
5482
5483 static boolean checkIfAllPlayersFitToScreen_RND()
5484 {
5485   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5486
5487   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5488
5489   return (sx2 - sx1 < SCR_FIELDX &&
5490           sy2 - sy1 < SCR_FIELDY);
5491 }
5492
5493 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5494 {
5495   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5496
5497   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5498
5499   *sx = (sx1 + sx2) / 2;
5500   *sy = (sy1 + sy2) / 2;
5501 }
5502
5503 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5504                         boolean center_screen, boolean quick_relocation)
5505 {
5506   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5507   boolean no_delay = (tape.warp_forward);
5508   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5509   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5510
5511   if (quick_relocation)
5512   {
5513     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5514     {
5515       if (!level.shifted_relocation || center_screen)
5516       {
5517         /* quick relocation (without scrolling), with centering of screen */
5518
5519         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5520                     x > SBX_Right + MIDPOSX ? SBX_Right :
5521                     x - MIDPOSX);
5522
5523         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5524                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5525                     y - MIDPOSY);
5526       }
5527       else
5528       {
5529         /* quick relocation (without scrolling), but do not center screen */
5530
5531         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5532                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5533                                old_x - MIDPOSX);
5534
5535         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5536                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5537                                old_y - MIDPOSY);
5538
5539         int offset_x = x + (scroll_x - center_scroll_x);
5540         int offset_y = y + (scroll_y - center_scroll_y);
5541
5542         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5543                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5544                     offset_x - MIDPOSX);
5545
5546         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5547                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5548                     offset_y - MIDPOSY);
5549       }
5550     }
5551     else
5552     {
5553 #if 1
5554       if (!level.shifted_relocation || center_screen)
5555       {
5556         /* quick relocation (without scrolling), with centering of screen */
5557
5558         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5559                     x > SBX_Right + MIDPOSX ? SBX_Right :
5560                     x - MIDPOSX);
5561
5562         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5564                     y - MIDPOSY);
5565       }
5566       else
5567       {
5568         /* quick relocation (without scrolling), but do not center screen */
5569
5570         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5571                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5572                                old_x - MIDPOSX);
5573
5574         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5575                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5576                                old_y - MIDPOSY);
5577
5578         int offset_x = x + (scroll_x - center_scroll_x);
5579         int offset_y = y + (scroll_y - center_scroll_y);
5580
5581         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5582                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5583                     offset_x - MIDPOSX);
5584
5585         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5586                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587                     offset_y - MIDPOSY);
5588       }
5589 #else
5590       /* quick relocation (without scrolling), inside visible screen area */
5591
5592       int offset = game.scroll_delay_value;
5593
5594       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5595           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5596         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5597
5598       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5599           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5600         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5601
5602       /* don't scroll over playfield boundaries */
5603       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5604         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5605
5606       /* don't scroll over playfield boundaries */
5607       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5608         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5609 #endif
5610     }
5611
5612     RedrawPlayfield(TRUE, 0,0,0,0);
5613   }
5614   else
5615   {
5616 #if 1
5617     int scroll_xx, scroll_yy;
5618
5619     if (!level.shifted_relocation || center_screen)
5620     {
5621       /* visible relocation (with scrolling), with centering of screen */
5622
5623       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5624                    x > SBX_Right + MIDPOSX ? SBX_Right :
5625                    x - MIDPOSX);
5626
5627       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5628                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5629                    y - MIDPOSY);
5630     }
5631     else
5632     {
5633       /* visible relocation (with scrolling), but do not center screen */
5634
5635       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5636                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5637                              old_x - MIDPOSX);
5638
5639       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5640                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5641                              old_y - MIDPOSY);
5642
5643       int offset_x = x + (scroll_x - center_scroll_x);
5644       int offset_y = y + (scroll_y - center_scroll_y);
5645
5646       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5647                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5648                    offset_x - MIDPOSX);
5649
5650       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5651                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5652                    offset_y - MIDPOSY);
5653     }
5654
5655 #else
5656
5657     /* visible relocation (with scrolling), with centering of screen */
5658
5659     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5660                      x > SBX_Right + MIDPOSX ? SBX_Right :
5661                      x - MIDPOSX);
5662
5663     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5665                      y - MIDPOSY);
5666 #endif
5667
5668     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5669
5670     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5671     {
5672       int dx = 0, dy = 0;
5673       int fx = FX, fy = FY;
5674
5675       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5676       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5677
5678       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5679         break;
5680
5681       scroll_x -= dx;
5682       scroll_y -= dy;
5683
5684       fx += dx * TILEX / 2;
5685       fy += dy * TILEY / 2;
5686
5687       ScrollLevel(dx, dy);
5688       DrawAllPlayers();
5689
5690       /* scroll in two steps of half tile size to make things smoother */
5691       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5692       FlushDisplay();
5693       Delay(wait_delay_value);
5694
5695       /* scroll second step to align at full tile size */
5696       BackToFront();
5697       Delay(wait_delay_value);
5698     }
5699
5700     DrawAllPlayers();
5701     BackToFront();
5702     Delay(wait_delay_value);
5703   }
5704 }
5705
5706 void RelocatePlayer(int jx, int jy, int el_player_raw)
5707 {
5708   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5709   int player_nr = GET_PLAYER_NR(el_player);
5710   struct PlayerInfo *player = &stored_player[player_nr];
5711   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5712   boolean no_delay = (tape.warp_forward);
5713   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5714   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5715   int old_jx = player->jx;
5716   int old_jy = player->jy;
5717   int old_element = Feld[old_jx][old_jy];
5718   int element = Feld[jx][jy];
5719   boolean player_relocated = (old_jx != jx || old_jy != jy);
5720
5721   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5722   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5723   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5724   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5725   int leave_side_horiz = move_dir_horiz;
5726   int leave_side_vert  = move_dir_vert;
5727   int enter_side = enter_side_horiz | enter_side_vert;
5728   int leave_side = leave_side_horiz | leave_side_vert;
5729
5730   if (player->GameOver)         /* do not reanimate dead player */
5731     return;
5732
5733   if (!player_relocated)        /* no need to relocate the player */
5734     return;
5735
5736   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5737   {
5738     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5739     DrawLevelField(jx, jy);
5740   }
5741
5742   if (player->present)
5743   {
5744     while (player->MovPos)
5745     {
5746       ScrollPlayer(player, SCROLL_GO_ON);
5747       ScrollScreen(NULL, SCROLL_GO_ON);
5748
5749       AdvanceFrameAndPlayerCounters(player->index_nr);
5750
5751       DrawPlayer(player);
5752
5753       BackToFront();
5754       Delay(wait_delay_value);
5755     }
5756
5757     DrawPlayer(player);         /* needed here only to cleanup last field */
5758     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5759
5760     player->is_moving = FALSE;
5761   }
5762
5763   if (IS_CUSTOM_ELEMENT(old_element))
5764     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5765                                CE_LEFT_BY_PLAYER,
5766                                player->index_bit, leave_side);
5767
5768   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5769                                       CE_PLAYER_LEAVES_X,
5770                                       player->index_bit, leave_side);
5771
5772   Feld[jx][jy] = el_player;
5773   InitPlayerField(jx, jy, el_player, TRUE);
5774
5775   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5776   {
5777     Feld[jx][jy] = element;
5778     InitField(jx, jy, FALSE);
5779   }
5780
5781   /* only visually relocate centered player */
5782   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5783                      FALSE, level.instant_relocation);
5784
5785   TestIfPlayerTouchesBadThing(jx, jy);
5786   TestIfPlayerTouchesCustomElement(jx, jy);
5787
5788   if (IS_CUSTOM_ELEMENT(element))
5789     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5790                                player->index_bit, enter_side);
5791
5792   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5793                                       player->index_bit, enter_side);
5794
5795 #if 1
5796   if (player->is_switching)
5797   {
5798     /* ensure that relocation while still switching an element does not cause
5799        a new element to be treated as also switched directly after relocation
5800        (this is important for teleporter switches that teleport the player to
5801        a place where another teleporter switch is in the same direction, which
5802        would then incorrectly be treated as immediately switched before the
5803        direction key that caused the switch was released) */
5804
5805     player->switch_x += jx - old_jx;
5806     player->switch_y += jy - old_jy;
5807   }
5808 #endif
5809 }
5810
5811 void Explode(int ex, int ey, int phase, int mode)
5812 {
5813   int x, y;
5814   int last_phase;
5815   int border_element;
5816
5817   /* !!! eliminate this variable !!! */
5818   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5819
5820   if (game.explosions_delayed)
5821   {
5822     ExplodeField[ex][ey] = mode;
5823     return;
5824   }
5825
5826   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5827   {
5828     int center_element = Feld[ex][ey];
5829     int artwork_element, explosion_element;     /* set these values later */
5830
5831 #if 0
5832     /* --- This is only really needed (and now handled) in "Impact()". --- */
5833     /* do not explode moving elements that left the explode field in time */
5834     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5835         center_element == EL_EMPTY &&
5836         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5837       return;
5838 #endif
5839
5840 #if 0
5841     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5842     if (mode == EX_TYPE_NORMAL ||
5843         mode == EX_TYPE_CENTER ||
5844         mode == EX_TYPE_CROSS)
5845       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5846 #endif
5847
5848     /* remove things displayed in background while burning dynamite */
5849     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5850       Back[ex][ey] = 0;
5851
5852     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5853     {
5854       /* put moving element to center field (and let it explode there) */
5855       center_element = MovingOrBlocked2Element(ex, ey);
5856       RemoveMovingField(ex, ey);
5857       Feld[ex][ey] = center_element;
5858     }
5859
5860     /* now "center_element" is finally determined -- set related values now */
5861     artwork_element = center_element;           /* for custom player artwork */
5862     explosion_element = center_element;         /* for custom player artwork */
5863
5864     if (IS_PLAYER(ex, ey))
5865     {
5866       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5867
5868       artwork_element = stored_player[player_nr].artwork_element;
5869
5870       if (level.use_explosion_element[player_nr])
5871       {
5872         explosion_element = level.explosion_element[player_nr];
5873         artwork_element = explosion_element;
5874       }
5875     }
5876
5877 #if 1
5878     if (mode == EX_TYPE_NORMAL ||
5879         mode == EX_TYPE_CENTER ||
5880         mode == EX_TYPE_CROSS)
5881       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5882 #endif
5883
5884     last_phase = element_info[explosion_element].explosion_delay + 1;
5885
5886     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5887     {
5888       int xx = x - ex + 1;
5889       int yy = y - ey + 1;
5890       int element;
5891
5892       if (!IN_LEV_FIELD(x, y) ||
5893           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5894           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5895         continue;
5896
5897       element = Feld[x][y];
5898
5899       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5900       {
5901         element = MovingOrBlocked2Element(x, y);
5902
5903         if (!IS_EXPLOSION_PROOF(element))
5904           RemoveMovingField(x, y);
5905       }
5906
5907       /* indestructible elements can only explode in center (but not flames) */
5908       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5909                                            mode == EX_TYPE_BORDER)) ||
5910           element == EL_FLAMES)
5911         continue;
5912
5913       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5914          behaviour, for example when touching a yamyam that explodes to rocks
5915          with active deadly shield, a rock is created under the player !!! */
5916       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5917 #if 0
5918       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5919           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5920            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5921 #else
5922       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5923 #endif
5924       {
5925         if (IS_ACTIVE_BOMB(element))
5926         {
5927           /* re-activate things under the bomb like gate or penguin */
5928           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5929           Back[x][y] = 0;
5930         }
5931
5932         continue;
5933       }
5934
5935       /* save walkable background elements while explosion on same tile */
5936       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5937           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5938         Back[x][y] = element;
5939
5940       /* ignite explodable elements reached by other explosion */
5941       if (element == EL_EXPLOSION)
5942         element = Store2[x][y];
5943
5944       if (AmoebaNr[x][y] &&
5945           (element == EL_AMOEBA_FULL ||
5946            element == EL_BD_AMOEBA ||
5947            element == EL_AMOEBA_GROWING))
5948       {
5949         AmoebaCnt[AmoebaNr[x][y]]--;
5950         AmoebaCnt2[AmoebaNr[x][y]]--;
5951       }
5952
5953       RemoveField(x, y);
5954
5955       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5956       {
5957         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5958
5959         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5960
5961         if (PLAYERINFO(ex, ey)->use_murphy)
5962           Store[x][y] = EL_EMPTY;
5963       }
5964
5965       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5966          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5967       else if (ELEM_IS_PLAYER(center_element))
5968         Store[x][y] = EL_EMPTY;
5969       else if (center_element == EL_YAMYAM)
5970         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5971       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5972         Store[x][y] = element_info[center_element].content.e[xx][yy];
5973 #if 1
5974       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5975          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5976          otherwise) -- FIX THIS !!! */
5977       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5978         Store[x][y] = element_info[element].content.e[1][1];
5979 #else
5980       else if (!CAN_EXPLODE(element))
5981         Store[x][y] = element_info[element].content.e[1][1];
5982 #endif
5983       else
5984         Store[x][y] = EL_EMPTY;
5985
5986       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5987           center_element == EL_AMOEBA_TO_DIAMOND)
5988         Store2[x][y] = element;
5989
5990       Feld[x][y] = EL_EXPLOSION;
5991       GfxElement[x][y] = artwork_element;
5992
5993       ExplodePhase[x][y] = 1;
5994       ExplodeDelay[x][y] = last_phase;
5995
5996       Stop[x][y] = TRUE;
5997     }
5998
5999     if (center_element == EL_YAMYAM)
6000       game.yamyam_content_nr =
6001         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6002
6003     return;
6004   }
6005
6006   if (Stop[ex][ey])
6007     return;
6008
6009   x = ex;
6010   y = ey;
6011
6012   if (phase == 1)
6013     GfxFrame[x][y] = 0;         /* restart explosion animation */
6014
6015   last_phase = ExplodeDelay[x][y];
6016
6017   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6018
6019 #ifdef DEBUG
6020
6021   /* activate this even in non-DEBUG version until cause for crash in
6022      getGraphicAnimationFrame() (see below) is found and eliminated */
6023
6024 #endif
6025 #if 1
6026
6027 #if 1
6028   /* this can happen if the player leaves an explosion just in time */
6029   if (GfxElement[x][y] == EL_UNDEFINED)
6030     GfxElement[x][y] = EL_EMPTY;
6031 #else
6032   if (GfxElement[x][y] == EL_UNDEFINED)
6033   {
6034     printf("\n\n");
6035     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6036     printf("Explode(): This should never happen!\n");
6037     printf("\n\n");
6038
6039     GfxElement[x][y] = EL_EMPTY;
6040   }
6041 #endif
6042
6043 #endif
6044
6045   border_element = Store2[x][y];
6046   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047     border_element = StorePlayer[x][y];
6048
6049   if (phase == element_info[border_element].ignition_delay ||
6050       phase == last_phase)
6051   {
6052     boolean border_explosion = FALSE;
6053
6054     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055         !PLAYER_EXPLOSION_PROTECTED(x, y))
6056     {
6057       KillPlayerUnlessExplosionProtected(x, y);
6058       border_explosion = TRUE;
6059     }
6060     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6061     {
6062       Feld[x][y] = Store2[x][y];
6063       Store2[x][y] = 0;
6064       Bang(x, y);
6065       border_explosion = TRUE;
6066     }
6067     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6068     {
6069       AmoebeUmwandeln(x, y);
6070       Store2[x][y] = 0;
6071       border_explosion = TRUE;
6072     }
6073
6074     /* if an element just explodes due to another explosion (chain-reaction),
6075        do not immediately end the new explosion when it was the last frame of
6076        the explosion (as it would be done in the following "if"-statement!) */
6077     if (border_explosion && phase == last_phase)
6078       return;
6079   }
6080
6081   if (phase == last_phase)
6082   {
6083     int element;
6084
6085     element = Feld[x][y] = Store[x][y];
6086     Store[x][y] = Store2[x][y] = 0;
6087     GfxElement[x][y] = EL_UNDEFINED;
6088
6089     /* player can escape from explosions and might therefore be still alive */
6090     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6091         element <= EL_PLAYER_IS_EXPLODING_4)
6092     {
6093       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6094       int explosion_element = EL_PLAYER_1 + player_nr;
6095       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6096       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6097
6098       if (level.use_explosion_element[player_nr])
6099         explosion_element = level.explosion_element[player_nr];
6100
6101       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6102                     element_info[explosion_element].content.e[xx][yy]);
6103     }
6104
6105     /* restore probably existing indestructible background element */
6106     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6107       element = Feld[x][y] = Back[x][y];
6108     Back[x][y] = 0;
6109
6110     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6111     GfxDir[x][y] = MV_NONE;
6112     ChangeDelay[x][y] = 0;
6113     ChangePage[x][y] = -1;
6114
6115 #if USE_NEW_CUSTOM_VALUE
6116     CustomValue[x][y] = 0;
6117 #endif
6118
6119     InitField_WithBug2(x, y, FALSE);
6120
6121     TEST_DrawLevelField(x, y);
6122
6123     TestIfElementTouchesCustomElement(x, y);
6124
6125     if (GFX_CRUMBLED(element))
6126       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6127
6128     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6129       StorePlayer[x][y] = 0;
6130
6131     if (ELEM_IS_PLAYER(element))
6132       RelocatePlayer(x, y, element);
6133   }
6134   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6135   {
6136     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6137     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6138
6139     if (phase == delay)
6140       TEST_DrawLevelFieldCrumbledSand(x, y);
6141
6142     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6143     {
6144       DrawLevelElement(x, y, Back[x][y]);
6145       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6146     }
6147     else if (IS_WALKABLE_UNDER(Back[x][y]))
6148     {
6149       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6150       DrawLevelElementThruMask(x, y, Back[x][y]);
6151     }
6152     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6153       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6154   }
6155 }
6156
6157 void DynaExplode(int ex, int ey)
6158 {
6159   int i, j;
6160   int dynabomb_element = Feld[ex][ey];
6161   int dynabomb_size = 1;
6162   boolean dynabomb_xl = FALSE;
6163   struct PlayerInfo *player;
6164   static int xy[4][2] =
6165   {
6166     { 0, -1 },
6167     { -1, 0 },
6168     { +1, 0 },
6169     { 0, +1 }
6170   };
6171
6172   if (IS_ACTIVE_BOMB(dynabomb_element))
6173   {
6174     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6175     dynabomb_size = player->dynabomb_size;
6176     dynabomb_xl = player->dynabomb_xl;
6177     player->dynabombs_left++;
6178   }
6179
6180   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6181
6182   for (i = 0; i < NUM_DIRECTIONS; i++)
6183   {
6184     for (j = 1; j <= dynabomb_size; j++)
6185     {
6186       int x = ex + j * xy[i][0];
6187       int y = ey + j * xy[i][1];
6188       int element;
6189
6190       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6191         break;
6192
6193       element = Feld[x][y];
6194
6195       /* do not restart explosions of fields with active bombs */
6196       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6197         continue;
6198
6199       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6200
6201       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6202           !IS_DIGGABLE(element) && !dynabomb_xl)
6203         break;
6204     }
6205   }
6206 }
6207
6208 void Bang(int x, int y)
6209 {
6210   int element = MovingOrBlocked2Element(x, y);
6211   int explosion_type = EX_TYPE_NORMAL;
6212
6213   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6214   {
6215     struct PlayerInfo *player = PLAYERINFO(x, y);
6216
6217 #if USE_FIX_CE_ACTION_WITH_PLAYER
6218     element = Feld[x][y] = player->initial_element;
6219 #else
6220     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6221                             player->element_nr);
6222 #endif
6223
6224     if (level.use_explosion_element[player->index_nr])
6225     {
6226       int explosion_element = level.explosion_element[player->index_nr];
6227
6228       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6229         explosion_type = EX_TYPE_CROSS;
6230       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6231         explosion_type = EX_TYPE_CENTER;
6232     }
6233   }
6234
6235   switch (element)
6236   {
6237     case EL_BUG:
6238     case EL_SPACESHIP:
6239     case EL_BD_BUTTERFLY:
6240     case EL_BD_FIREFLY:
6241     case EL_YAMYAM:
6242     case EL_DARK_YAMYAM:
6243     case EL_ROBOT:
6244     case EL_PACMAN:
6245     case EL_MOLE:
6246       RaiseScoreElement(element);
6247       break;
6248
6249     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6250     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6251     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6252     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6253     case EL_DYNABOMB_INCREASE_NUMBER:
6254     case EL_DYNABOMB_INCREASE_SIZE:
6255     case EL_DYNABOMB_INCREASE_POWER:
6256       explosion_type = EX_TYPE_DYNA;
6257       break;
6258
6259     case EL_DC_LANDMINE:
6260 #if 0
6261     case EL_EM_EXIT_OPEN:
6262     case EL_EM_STEEL_EXIT_OPEN:
6263 #endif
6264       explosion_type = EX_TYPE_CENTER;
6265       break;
6266
6267     case EL_PENGUIN:
6268     case EL_LAMP:
6269     case EL_LAMP_ACTIVE:
6270     case EL_AMOEBA_TO_DIAMOND:
6271       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
6272         explosion_type = EX_TYPE_CENTER;
6273       break;
6274
6275     default:
6276       if (element_info[element].explosion_type == EXPLODES_CROSS)
6277         explosion_type = EX_TYPE_CROSS;
6278       else if (element_info[element].explosion_type == EXPLODES_1X1)
6279         explosion_type = EX_TYPE_CENTER;
6280       break;
6281   }
6282
6283   if (explosion_type == EX_TYPE_DYNA)
6284     DynaExplode(x, y);
6285   else
6286     Explode(x, y, EX_PHASE_START, explosion_type);
6287
6288   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6289 }
6290
6291 void SplashAcid(int x, int y)
6292 {
6293   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6294       (!IN_LEV_FIELD(x - 1, y - 2) ||
6295        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6296     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6297
6298   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6299       (!IN_LEV_FIELD(x + 1, y - 2) ||
6300        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6301     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6302
6303   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6304 }
6305
6306 static void InitBeltMovement()
6307 {
6308   static int belt_base_element[4] =
6309   {
6310     EL_CONVEYOR_BELT_1_LEFT,
6311     EL_CONVEYOR_BELT_2_LEFT,
6312     EL_CONVEYOR_BELT_3_LEFT,
6313     EL_CONVEYOR_BELT_4_LEFT
6314   };
6315   static int belt_base_active_element[4] =
6316   {
6317     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6318     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6319     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6320     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6321   };
6322
6323   int x, y, i, j;
6324
6325   /* set frame order for belt animation graphic according to belt direction */
6326   for (i = 0; i < NUM_BELTS; i++)
6327   {
6328     int belt_nr = i;
6329
6330     for (j = 0; j < NUM_BELT_PARTS; j++)
6331     {
6332       int element = belt_base_active_element[belt_nr] + j;
6333       int graphic_1 = el2img(element);
6334       int graphic_2 = el2panelimg(element);
6335
6336       if (game.belt_dir[i] == MV_LEFT)
6337       {
6338         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6339         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6340       }
6341       else
6342       {
6343         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6344         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6345       }
6346     }
6347   }
6348
6349   SCAN_PLAYFIELD(x, y)
6350   {
6351     int element = Feld[x][y];
6352
6353     for (i = 0; i < NUM_BELTS; i++)
6354     {
6355       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6356       {
6357         int e_belt_nr = getBeltNrFromBeltElement(element);
6358         int belt_nr = i;
6359
6360         if (e_belt_nr == belt_nr)
6361         {
6362           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6363
6364           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6365         }
6366       }
6367     }
6368   }
6369 }
6370
6371 static void ToggleBeltSwitch(int x, int y)
6372 {
6373   static int belt_base_element[4] =
6374   {
6375     EL_CONVEYOR_BELT_1_LEFT,
6376     EL_CONVEYOR_BELT_2_LEFT,
6377     EL_CONVEYOR_BELT_3_LEFT,
6378     EL_CONVEYOR_BELT_4_LEFT
6379   };
6380   static int belt_base_active_element[4] =
6381   {
6382     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6383     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6384     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6385     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6386   };
6387   static int belt_base_switch_element[4] =
6388   {
6389     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6390     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6391     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6392     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6393   };
6394   static int belt_move_dir[4] =
6395   {
6396     MV_LEFT,
6397     MV_NONE,
6398     MV_RIGHT,
6399     MV_NONE,
6400   };
6401
6402   int element = Feld[x][y];
6403   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6404   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6405   int belt_dir = belt_move_dir[belt_dir_nr];
6406   int xx, yy, i;
6407
6408   if (!IS_BELT_SWITCH(element))
6409     return;
6410
6411   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6412   game.belt_dir[belt_nr] = belt_dir;
6413
6414   if (belt_dir_nr == 3)
6415     belt_dir_nr = 1;
6416
6417   /* set frame order for belt animation graphic according to belt direction */
6418   for (i = 0; i < NUM_BELT_PARTS; i++)
6419   {
6420     int element = belt_base_active_element[belt_nr] + i;
6421     int graphic_1 = el2img(element);
6422     int graphic_2 = el2panelimg(element);
6423
6424     if (belt_dir == MV_LEFT)
6425     {
6426       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6427       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6428     }
6429     else
6430     {
6431       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6432       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6433     }
6434   }
6435
6436   SCAN_PLAYFIELD(xx, yy)
6437   {
6438     int element = Feld[xx][yy];
6439
6440     if (IS_BELT_SWITCH(element))
6441     {
6442       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6443
6444       if (e_belt_nr == belt_nr)
6445       {
6446         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6447         TEST_DrawLevelField(xx, yy);
6448       }
6449     }
6450     else if (IS_BELT(element) && belt_dir != MV_NONE)
6451     {
6452       int e_belt_nr = getBeltNrFromBeltElement(element);
6453
6454       if (e_belt_nr == belt_nr)
6455       {
6456         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6457
6458         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6459         TEST_DrawLevelField(xx, yy);
6460       }
6461     }
6462     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6463     {
6464       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6465
6466       if (e_belt_nr == belt_nr)
6467       {
6468         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6469
6470         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6471         TEST_DrawLevelField(xx, yy);
6472       }
6473     }
6474   }
6475 }
6476
6477 static void ToggleSwitchgateSwitch(int x, int y)
6478 {
6479   int xx, yy;
6480
6481   game.switchgate_pos = !game.switchgate_pos;
6482
6483   SCAN_PLAYFIELD(xx, yy)
6484   {
6485     int element = Feld[xx][yy];
6486
6487 #if !USE_BOTH_SWITCHGATE_SWITCHES
6488     if (element == EL_SWITCHGATE_SWITCH_UP ||
6489         element == EL_SWITCHGATE_SWITCH_DOWN)
6490     {
6491       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6492       TEST_DrawLevelField(xx, yy);
6493     }
6494     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6495              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6496     {
6497       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6498       TEST_DrawLevelField(xx, yy);
6499     }
6500 #else
6501     if (element == EL_SWITCHGATE_SWITCH_UP)
6502     {
6503       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6504       TEST_DrawLevelField(xx, yy);
6505     }
6506     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6507     {
6508       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6509       TEST_DrawLevelField(xx, yy);
6510     }
6511     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6512     {
6513       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6514       TEST_DrawLevelField(xx, yy);
6515     }
6516     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6517     {
6518       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6519       TEST_DrawLevelField(xx, yy);
6520     }
6521 #endif
6522     else if (element == EL_SWITCHGATE_OPEN ||
6523              element == EL_SWITCHGATE_OPENING)
6524     {
6525       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6526
6527       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6528     }
6529     else if (element == EL_SWITCHGATE_CLOSED ||
6530              element == EL_SWITCHGATE_CLOSING)
6531     {
6532       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6533
6534       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6535     }
6536   }
6537 }
6538
6539 static int getInvisibleActiveFromInvisibleElement(int element)
6540 {
6541   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6542           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6543           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6544           element);
6545 }
6546
6547 static int getInvisibleFromInvisibleActiveElement(int element)
6548 {
6549   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6550           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6551           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6552           element);
6553 }
6554
6555 static void RedrawAllLightSwitchesAndInvisibleElements()
6556 {
6557   int x, y;
6558
6559   SCAN_PLAYFIELD(x, y)
6560   {
6561     int element = Feld[x][y];
6562
6563     if (element == EL_LIGHT_SWITCH &&
6564         game.light_time_left > 0)
6565     {
6566       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6567       TEST_DrawLevelField(x, y);
6568     }
6569     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6570              game.light_time_left == 0)
6571     {
6572       Feld[x][y] = EL_LIGHT_SWITCH;
6573       TEST_DrawLevelField(x, y);
6574     }
6575     else if (element == EL_EMC_DRIPPER &&
6576              game.light_time_left > 0)
6577     {
6578       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6579       TEST_DrawLevelField(x, y);
6580     }
6581     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6582              game.light_time_left == 0)
6583     {
6584       Feld[x][y] = EL_EMC_DRIPPER;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (element == EL_INVISIBLE_STEELWALL ||
6588              element == EL_INVISIBLE_WALL ||
6589              element == EL_INVISIBLE_SAND)
6590     {
6591       if (game.light_time_left > 0)
6592         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6593
6594       TEST_DrawLevelField(x, y);
6595
6596       /* uncrumble neighbour fields, if needed */
6597       if (element == EL_INVISIBLE_SAND)
6598         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6599     }
6600     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6601              element == EL_INVISIBLE_WALL_ACTIVE ||
6602              element == EL_INVISIBLE_SAND_ACTIVE)
6603     {
6604       if (game.light_time_left == 0)
6605         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6606
6607       TEST_DrawLevelField(x, y);
6608
6609       /* re-crumble neighbour fields, if needed */
6610       if (element == EL_INVISIBLE_SAND)
6611         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6612     }
6613   }
6614 }
6615
6616 static void RedrawAllInvisibleElementsForLenses()
6617 {
6618   int x, y;
6619
6620   SCAN_PLAYFIELD(x, y)
6621   {
6622     int element = Feld[x][y];
6623
6624     if (element == EL_EMC_DRIPPER &&
6625         game.lenses_time_left > 0)
6626     {
6627       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6628       TEST_DrawLevelField(x, y);
6629     }
6630     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6631              game.lenses_time_left == 0)
6632     {
6633       Feld[x][y] = EL_EMC_DRIPPER;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_INVISIBLE_STEELWALL ||
6637              element == EL_INVISIBLE_WALL ||
6638              element == EL_INVISIBLE_SAND)
6639     {
6640       if (game.lenses_time_left > 0)
6641         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6642
6643       TEST_DrawLevelField(x, y);
6644
6645       /* uncrumble neighbour fields, if needed */
6646       if (element == EL_INVISIBLE_SAND)
6647         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6648     }
6649     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6650              element == EL_INVISIBLE_WALL_ACTIVE ||
6651              element == EL_INVISIBLE_SAND_ACTIVE)
6652     {
6653       if (game.lenses_time_left == 0)
6654         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6655
6656       TEST_DrawLevelField(x, y);
6657
6658       /* re-crumble neighbour fields, if needed */
6659       if (element == EL_INVISIBLE_SAND)
6660         TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6661     }
6662   }
6663 }
6664
6665 static void RedrawAllInvisibleElementsForMagnifier()
6666 {
6667   int x, y;
6668
6669   SCAN_PLAYFIELD(x, y)
6670   {
6671     int element = Feld[x][y];
6672
6673     if (element == EL_EMC_FAKE_GRASS &&
6674         game.magnify_time_left > 0)
6675     {
6676       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6677       TEST_DrawLevelField(x, y);
6678     }
6679     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6680              game.magnify_time_left == 0)
6681     {
6682       Feld[x][y] = EL_EMC_FAKE_GRASS;
6683       TEST_DrawLevelField(x, y);
6684     }
6685     else if (IS_GATE_GRAY(element) &&
6686              game.magnify_time_left > 0)
6687     {
6688       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6689                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6690                     IS_EM_GATE_GRAY(element) ?
6691                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6692                     IS_EMC_GATE_GRAY(element) ?
6693                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6694                     IS_DC_GATE_GRAY(element) ?
6695                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6696                     element);
6697       TEST_DrawLevelField(x, y);
6698     }
6699     else if (IS_GATE_GRAY_ACTIVE(element) &&
6700              game.magnify_time_left == 0)
6701     {
6702       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6703                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6704                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6705                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6706                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6707                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6708                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6709                     EL_DC_GATE_WHITE_GRAY :
6710                     element);
6711       TEST_DrawLevelField(x, y);
6712     }
6713   }
6714 }
6715
6716 static void ToggleLightSwitch(int x, int y)
6717 {
6718   int element = Feld[x][y];
6719
6720   game.light_time_left =
6721     (element == EL_LIGHT_SWITCH ?
6722      level.time_light * FRAMES_PER_SECOND : 0);
6723
6724   RedrawAllLightSwitchesAndInvisibleElements();
6725 }
6726
6727 static void ActivateTimegateSwitch(int x, int y)
6728 {
6729   int xx, yy;
6730
6731   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6732
6733   SCAN_PLAYFIELD(xx, yy)
6734   {
6735     int element = Feld[xx][yy];
6736
6737     if (element == EL_TIMEGATE_CLOSED ||
6738         element == EL_TIMEGATE_CLOSING)
6739     {
6740       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6741       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6742     }
6743
6744     /*
6745     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6746     {
6747       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6748       TEST_DrawLevelField(xx, yy);
6749     }
6750     */
6751
6752   }
6753
6754 #if 1
6755   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6756                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6757 #else
6758   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6759 #endif
6760 }
6761
6762 void Impact(int x, int y)
6763 {
6764   boolean last_line = (y == lev_fieldy - 1);
6765   boolean object_hit = FALSE;
6766   boolean impact = (last_line || object_hit);
6767   int element = Feld[x][y];
6768   int smashed = EL_STEELWALL;
6769
6770   if (!last_line)       /* check if element below was hit */
6771   {
6772     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6773       return;
6774
6775     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6776                                          MovDir[x][y + 1] != MV_DOWN ||
6777                                          MovPos[x][y + 1] <= TILEY / 2));
6778
6779     /* do not smash moving elements that left the smashed field in time */
6780     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6781         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6782       object_hit = FALSE;
6783
6784 #if USE_QUICKSAND_IMPACT_BUGFIX
6785     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6786     {
6787       RemoveMovingField(x, y + 1);
6788       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6789       Feld[x][y + 2] = EL_ROCK;
6790       TEST_DrawLevelField(x, y + 2);
6791
6792       object_hit = TRUE;
6793     }
6794
6795     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6796     {
6797       RemoveMovingField(x, y + 1);
6798       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6799       Feld[x][y + 2] = EL_ROCK;
6800       TEST_DrawLevelField(x, y + 2);
6801
6802       object_hit = TRUE;
6803     }
6804 #endif
6805
6806     if (object_hit)
6807       smashed = MovingOrBlocked2Element(x, y + 1);
6808
6809     impact = (last_line || object_hit);
6810   }
6811
6812   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6813   {
6814     SplashAcid(x, y + 1);
6815     return;
6816   }
6817
6818   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6819   /* only reset graphic animation if graphic really changes after impact */
6820   if (impact &&
6821       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6822   {
6823     ResetGfxAnimation(x, y);
6824     TEST_DrawLevelField(x, y);
6825   }
6826
6827   if (impact && CAN_EXPLODE_IMPACT(element))
6828   {
6829     Bang(x, y);
6830     return;
6831   }
6832   else if (impact && element == EL_PEARL &&
6833            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6834   {
6835     ResetGfxAnimation(x, y);
6836
6837     Feld[x][y] = EL_PEARL_BREAKING;
6838     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6839     return;
6840   }
6841   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6842   {
6843     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6844
6845     return;
6846   }
6847
6848   if (impact && element == EL_AMOEBA_DROP)
6849   {
6850     if (object_hit && IS_PLAYER(x, y + 1))
6851       KillPlayerUnlessEnemyProtected(x, y + 1);
6852     else if (object_hit && smashed == EL_PENGUIN)
6853       Bang(x, y + 1);
6854     else
6855     {
6856       Feld[x][y] = EL_AMOEBA_GROWING;
6857       Store[x][y] = EL_AMOEBA_WET;
6858
6859       ResetRandomAnimationValue(x, y);
6860     }
6861     return;
6862   }
6863
6864   if (object_hit)               /* check which object was hit */
6865   {
6866     if ((CAN_PASS_MAGIC_WALL(element) && 
6867          (smashed == EL_MAGIC_WALL ||
6868           smashed == EL_BD_MAGIC_WALL)) ||
6869         (CAN_PASS_DC_MAGIC_WALL(element) &&
6870          smashed == EL_DC_MAGIC_WALL))
6871     {
6872       int xx, yy;
6873       int activated_magic_wall =
6874         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6875          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6876          EL_DC_MAGIC_WALL_ACTIVE);
6877
6878       /* activate magic wall / mill */
6879       SCAN_PLAYFIELD(xx, yy)
6880       {
6881         if (Feld[xx][yy] == smashed)
6882           Feld[xx][yy] = activated_magic_wall;
6883       }
6884
6885       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6886       game.magic_wall_active = TRUE;
6887
6888       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6889                             SND_MAGIC_WALL_ACTIVATING :
6890                             smashed == EL_BD_MAGIC_WALL ?
6891                             SND_BD_MAGIC_WALL_ACTIVATING :
6892                             SND_DC_MAGIC_WALL_ACTIVATING));
6893     }
6894
6895     if (IS_PLAYER(x, y + 1))
6896     {
6897       if (CAN_SMASH_PLAYER(element))
6898       {
6899         KillPlayerUnlessEnemyProtected(x, y + 1);
6900         return;
6901       }
6902     }
6903     else if (smashed == EL_PENGUIN)
6904     {
6905       if (CAN_SMASH_PLAYER(element))
6906       {
6907         Bang(x, y + 1);
6908         return;
6909       }
6910     }
6911     else if (element == EL_BD_DIAMOND)
6912     {
6913       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6914       {
6915         Bang(x, y + 1);
6916         return;
6917       }
6918     }
6919     else if (((element == EL_SP_INFOTRON ||
6920                element == EL_SP_ZONK) &&
6921               (smashed == EL_SP_SNIKSNAK ||
6922                smashed == EL_SP_ELECTRON ||
6923                smashed == EL_SP_DISK_ORANGE)) ||
6924              (element == EL_SP_INFOTRON &&
6925               smashed == EL_SP_DISK_YELLOW))
6926     {
6927       Bang(x, y + 1);
6928       return;
6929     }
6930     else if (CAN_SMASH_EVERYTHING(element))
6931     {
6932       if (IS_CLASSIC_ENEMY(smashed) ||
6933           CAN_EXPLODE_SMASHED(smashed))
6934       {
6935         Bang(x, y + 1);
6936         return;
6937       }
6938       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6939       {
6940         if (smashed == EL_LAMP ||
6941             smashed == EL_LAMP_ACTIVE)
6942         {
6943           Bang(x, y + 1);
6944           return;
6945         }
6946         else if (smashed == EL_NUT)
6947         {
6948           Feld[x][y + 1] = EL_NUT_BREAKING;
6949           PlayLevelSound(x, y, SND_NUT_BREAKING);
6950           RaiseScoreElement(EL_NUT);
6951           return;
6952         }
6953         else if (smashed == EL_PEARL)
6954         {
6955           ResetGfxAnimation(x, y);
6956
6957           Feld[x][y + 1] = EL_PEARL_BREAKING;
6958           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6959           return;
6960         }
6961         else if (smashed == EL_DIAMOND)
6962         {
6963           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6964           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6965           return;
6966         }
6967         else if (IS_BELT_SWITCH(smashed))
6968         {
6969           ToggleBeltSwitch(x, y + 1);
6970         }
6971         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6972                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6973                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6974                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6975         {
6976           ToggleSwitchgateSwitch(x, y + 1);
6977         }
6978         else if (smashed == EL_LIGHT_SWITCH ||
6979                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6980         {
6981           ToggleLightSwitch(x, y + 1);
6982         }
6983         else
6984         {
6985 #if 0
6986           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6987 #endif
6988
6989           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6990
6991           CheckElementChangeBySide(x, y + 1, smashed, element,
6992                                    CE_SWITCHED, CH_SIDE_TOP);
6993           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6994                                             CH_SIDE_TOP);
6995         }
6996       }
6997       else
6998       {
6999         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7000       }
7001     }
7002   }
7003
7004   /* play sound of magic wall / mill */
7005   if (!last_line &&
7006       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7007        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7008        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7009   {
7010     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7011       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7012     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7013       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7014     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7015       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7016
7017     return;
7018   }
7019
7020   /* play sound of object that hits the ground */
7021   if (last_line || object_hit)
7022     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7023 }
7024
7025 inline static void TurnRoundExt(int x, int y)
7026 {
7027   static struct
7028   {
7029     int dx, dy;
7030   } move_xy[] =
7031   {
7032     {  0,  0 },
7033     { -1,  0 },
7034     { +1,  0 },
7035     {  0,  0 },
7036     {  0, -1 },
7037     {  0,  0 }, { 0, 0 }, { 0, 0 },
7038     {  0, +1 }
7039   };
7040   static struct
7041   {
7042     int left, right, back;
7043   } turn[] =
7044   {
7045     { 0,        0,              0        },
7046     { MV_DOWN,  MV_UP,          MV_RIGHT },
7047     { MV_UP,    MV_DOWN,        MV_LEFT  },
7048     { 0,        0,              0        },
7049     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7050     { 0,        0,              0        },
7051     { 0,        0,              0        },
7052     { 0,        0,              0        },
7053     { MV_RIGHT, MV_LEFT,        MV_UP    }
7054   };
7055
7056   int element = Feld[x][y];
7057   int move_pattern = element_info[element].move_pattern;
7058
7059   int old_move_dir = MovDir[x][y];
7060   int left_dir  = turn[old_move_dir].left;
7061   int right_dir = turn[old_move_dir].right;
7062   int back_dir  = turn[old_move_dir].back;
7063
7064   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7065   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7066   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7067   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7068
7069   int left_x  = x + left_dx,  left_y  = y + left_dy;
7070   int right_x = x + right_dx, right_y = y + right_dy;
7071   int move_x  = x + move_dx,  move_y  = y + move_dy;
7072
7073   int xx, yy;
7074
7075   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7076   {
7077     TestIfBadThingTouchesOtherBadThing(x, y);
7078
7079     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7080       MovDir[x][y] = right_dir;
7081     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7082       MovDir[x][y] = left_dir;
7083
7084     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7085       MovDelay[x][y] = 9;
7086     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7087       MovDelay[x][y] = 1;
7088   }
7089   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7090   {
7091     TestIfBadThingTouchesOtherBadThing(x, y);
7092
7093     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7094       MovDir[x][y] = left_dir;
7095     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7096       MovDir[x][y] = right_dir;
7097
7098     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7099       MovDelay[x][y] = 9;
7100     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
7101       MovDelay[x][y] = 1;
7102   }
7103   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7104   {
7105     TestIfBadThingTouchesOtherBadThing(x, y);
7106
7107     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7108       MovDir[x][y] = left_dir;
7109     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7110       MovDir[x][y] = right_dir;
7111
7112     if (MovDir[x][y] != old_move_dir)
7113       MovDelay[x][y] = 9;
7114   }
7115   else if (element == EL_YAMYAM)
7116   {
7117     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7118     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7119
7120     if (can_turn_left && can_turn_right)
7121       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7122     else if (can_turn_left)
7123       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7124     else if (can_turn_right)
7125       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7126     else
7127       MovDir[x][y] = back_dir;
7128
7129     MovDelay[x][y] = 16 + 16 * RND(3);
7130   }
7131   else if (element == EL_DARK_YAMYAM)
7132   {
7133     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7134                                                          left_x, left_y);
7135     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7136                                                          right_x, right_y);
7137
7138     if (can_turn_left && can_turn_right)
7139       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7140     else if (can_turn_left)
7141       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7142     else if (can_turn_right)
7143       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7144     else
7145       MovDir[x][y] = back_dir;
7146
7147     MovDelay[x][y] = 16 + 16 * RND(3);
7148   }
7149   else if (element == EL_PACMAN)
7150   {
7151     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7152     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7153
7154     if (can_turn_left && can_turn_right)
7155       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7156     else if (can_turn_left)
7157       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7158     else if (can_turn_right)
7159       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7160     else
7161       MovDir[x][y] = back_dir;
7162
7163     MovDelay[x][y] = 6 + RND(40);
7164   }
7165   else if (element == EL_PIG)
7166   {
7167     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7168     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7169     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7170     boolean should_turn_left, should_turn_right, should_move_on;
7171     int rnd_value = 24;
7172     int rnd = RND(rnd_value);
7173
7174     should_turn_left = (can_turn_left &&
7175                         (!can_move_on ||
7176                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7177                                                    y + back_dy + left_dy)));
7178     should_turn_right = (can_turn_right &&
7179                          (!can_move_on ||
7180                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7181                                                     y + back_dy + right_dy)));
7182     should_move_on = (can_move_on &&
7183                       (!can_turn_left ||
7184                        !can_turn_right ||
7185                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7186                                                  y + move_dy + left_dy) ||
7187                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7188                                                  y + move_dy + right_dy)));
7189
7190     if (should_turn_left || should_turn_right || should_move_on)
7191     {
7192       if (should_turn_left && should_turn_right && should_move_on)
7193         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7194                         rnd < 2 * rnd_value / 3 ? right_dir :
7195                         old_move_dir);
7196       else if (should_turn_left && should_turn_right)
7197         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7198       else if (should_turn_left && should_move_on)
7199         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7200       else if (should_turn_right && should_move_on)
7201         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7202       else if (should_turn_left)
7203         MovDir[x][y] = left_dir;
7204       else if (should_turn_right)
7205         MovDir[x][y] = right_dir;
7206       else if (should_move_on)
7207         MovDir[x][y] = old_move_dir;
7208     }
7209     else if (can_move_on && rnd > rnd_value / 8)
7210       MovDir[x][y] = old_move_dir;
7211     else if (can_turn_left && can_turn_right)
7212       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7213     else if (can_turn_left && rnd > rnd_value / 8)
7214       MovDir[x][y] = left_dir;
7215     else if (can_turn_right && rnd > rnd_value/8)
7216       MovDir[x][y] = right_dir;
7217     else
7218       MovDir[x][y] = back_dir;
7219
7220     xx = x + move_xy[MovDir[x][y]].dx;
7221     yy = y + move_xy[MovDir[x][y]].dy;
7222
7223     if (!IN_LEV_FIELD(xx, yy) ||
7224         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7225       MovDir[x][y] = old_move_dir;
7226
7227     MovDelay[x][y] = 0;
7228   }
7229   else if (element == EL_DRAGON)
7230   {
7231     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7232     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7233     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7234     int rnd_value = 24;
7235     int rnd = RND(rnd_value);
7236
7237     if (can_move_on && rnd > rnd_value / 8)
7238       MovDir[x][y] = old_move_dir;
7239     else if (can_turn_left && can_turn_right)
7240       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7241     else if (can_turn_left && rnd > rnd_value / 8)
7242       MovDir[x][y] = left_dir;
7243     else if (can_turn_right && rnd > rnd_value / 8)
7244       MovDir[x][y] = right_dir;
7245     else
7246       MovDir[x][y] = back_dir;
7247
7248     xx = x + move_xy[MovDir[x][y]].dx;
7249     yy = y + move_xy[MovDir[x][y]].dy;
7250
7251     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7252       MovDir[x][y] = old_move_dir;
7253
7254     MovDelay[x][y] = 0;
7255   }
7256   else if (element == EL_MOLE)
7257   {
7258     boolean can_move_on =
7259       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7260                             IS_AMOEBOID(Feld[move_x][move_y]) ||
7261                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7262     if (!can_move_on)
7263     {
7264       boolean can_turn_left =
7265         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7266                               IS_AMOEBOID(Feld[left_x][left_y])));
7267
7268       boolean can_turn_right =
7269         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7270                               IS_AMOEBOID(Feld[right_x][right_y])));
7271
7272       if (can_turn_left && can_turn_right)
7273         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7274       else if (can_turn_left)
7275         MovDir[x][y] = left_dir;
7276       else
7277         MovDir[x][y] = right_dir;
7278     }
7279
7280     if (MovDir[x][y] != old_move_dir)
7281       MovDelay[x][y] = 9;
7282   }
7283   else if (element == EL_BALLOON)
7284   {
7285     MovDir[x][y] = game.wind_direction;
7286     MovDelay[x][y] = 0;
7287   }
7288   else if (element == EL_SPRING)
7289   {
7290 #if USE_NEW_SPRING_BUMPER
7291     if (MovDir[x][y] & MV_HORIZONTAL)
7292     {
7293       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7294           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7295       {
7296         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7297         ResetGfxAnimation(move_x, move_y);
7298         TEST_DrawLevelField(move_x, move_y);
7299
7300         MovDir[x][y] = back_dir;
7301       }
7302       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7303                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304         MovDir[x][y] = MV_NONE;
7305     }
7306 #else
7307     if (MovDir[x][y] & MV_HORIZONTAL &&
7308         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7309          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7310       MovDir[x][y] = MV_NONE;
7311 #endif
7312
7313     MovDelay[x][y] = 0;
7314   }
7315   else if (element == EL_ROBOT ||
7316            element == EL_SATELLITE ||
7317            element == EL_PENGUIN ||
7318            element == EL_EMC_ANDROID)
7319   {
7320     int attr_x = -1, attr_y = -1;
7321
7322     if (AllPlayersGone)
7323     {
7324       attr_x = ExitX;
7325       attr_y = ExitY;
7326     }
7327     else
7328     {
7329       int i;
7330
7331       for (i = 0; i < MAX_PLAYERS; i++)
7332       {
7333         struct PlayerInfo *player = &stored_player[i];
7334         int jx = player->jx, jy = player->jy;
7335
7336         if (!player->active)
7337           continue;
7338
7339         if (attr_x == -1 ||
7340             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7341         {
7342           attr_x = jx;
7343           attr_y = jy;
7344         }
7345       }
7346     }
7347
7348     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7349         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7350          game.engine_version < VERSION_IDENT(3,1,0,0)))
7351     {
7352       attr_x = ZX;
7353       attr_y = ZY;
7354     }
7355
7356     if (element == EL_PENGUIN)
7357     {
7358       int i;
7359       static int xy[4][2] =
7360       {
7361         { 0, -1 },
7362         { -1, 0 },
7363         { +1, 0 },
7364         { 0, +1 }
7365       };
7366
7367       for (i = 0; i < NUM_DIRECTIONS; i++)
7368       {
7369         int ex = x + xy[i][0];
7370         int ey = y + xy[i][1];
7371
7372         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7373                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7374                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7375                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7376         {
7377           attr_x = ex;
7378           attr_y = ey;
7379           break;
7380         }
7381       }
7382     }
7383
7384     MovDir[x][y] = MV_NONE;
7385     if (attr_x < x)
7386       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7387     else if (attr_x > x)
7388       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7389     if (attr_y < y)
7390       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7391     else if (attr_y > y)
7392       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7393
7394     if (element == EL_ROBOT)
7395     {
7396       int newx, newy;
7397
7398       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7399         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7400       Moving2Blocked(x, y, &newx, &newy);
7401
7402       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7403         MovDelay[x][y] = 8 + 8 * !RND(3);
7404       else
7405         MovDelay[x][y] = 16;
7406     }
7407     else if (element == EL_PENGUIN)
7408     {
7409       int newx, newy;
7410
7411       MovDelay[x][y] = 1;
7412
7413       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7414       {
7415         boolean first_horiz = RND(2);
7416         int new_move_dir = MovDir[x][y];
7417
7418         MovDir[x][y] =
7419           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7420         Moving2Blocked(x, y, &newx, &newy);
7421
7422         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7423           return;
7424
7425         MovDir[x][y] =
7426           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7427         Moving2Blocked(x, y, &newx, &newy);
7428
7429         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7430           return;
7431
7432         MovDir[x][y] = old_move_dir;
7433         return;
7434       }
7435     }
7436     else if (element == EL_SATELLITE)
7437     {
7438       int newx, newy;
7439
7440       MovDelay[x][y] = 1;
7441
7442       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443       {
7444         boolean first_horiz = RND(2);
7445         int new_move_dir = MovDir[x][y];
7446
7447         MovDir[x][y] =
7448           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7449         Moving2Blocked(x, y, &newx, &newy);
7450
7451         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7452           return;
7453
7454         MovDir[x][y] =
7455           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456         Moving2Blocked(x, y, &newx, &newy);
7457
7458         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7459           return;
7460
7461         MovDir[x][y] = old_move_dir;
7462         return;
7463       }
7464     }
7465     else if (element == EL_EMC_ANDROID)
7466     {
7467       static int check_pos[16] =
7468       {
7469         -1,             /*  0 => (invalid)          */
7470         7,              /*  1 => MV_LEFT            */
7471         3,              /*  2 => MV_RIGHT           */
7472         -1,             /*  3 => (invalid)          */
7473         1,              /*  4 =>            MV_UP   */
7474         0,              /*  5 => MV_LEFT  | MV_UP   */
7475         2,              /*  6 => MV_RIGHT | MV_UP   */
7476         -1,             /*  7 => (invalid)          */
7477         5,              /*  8 =>            MV_DOWN */
7478         6,              /*  9 => MV_LEFT  | MV_DOWN */
7479         4,              /* 10 => MV_RIGHT | MV_DOWN */
7480         -1,             /* 11 => (invalid)          */
7481         -1,             /* 12 => (invalid)          */
7482         -1,             /* 13 => (invalid)          */
7483         -1,             /* 14 => (invalid)          */
7484         -1,             /* 15 => (invalid)          */
7485       };
7486       static struct
7487       {
7488         int dx, dy;
7489         int dir;
7490       } check_xy[8] =
7491       {
7492         { -1, -1,       MV_LEFT  | MV_UP   },
7493         {  0, -1,                  MV_UP   },
7494         { +1, -1,       MV_RIGHT | MV_UP   },
7495         { +1,  0,       MV_RIGHT           },
7496         { +1, +1,       MV_RIGHT | MV_DOWN },
7497         {  0, +1,                  MV_DOWN },
7498         { -1, +1,       MV_LEFT  | MV_DOWN },
7499         { -1,  0,       MV_LEFT            },
7500       };
7501       int start_pos, check_order;
7502       boolean can_clone = FALSE;
7503       int i;
7504
7505       /* check if there is any free field around current position */
7506       for (i = 0; i < 8; i++)
7507       {
7508         int newx = x + check_xy[i].dx;
7509         int newy = y + check_xy[i].dy;
7510
7511         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7512         {
7513           can_clone = TRUE;
7514
7515           break;
7516         }
7517       }
7518
7519       if (can_clone)            /* randomly find an element to clone */
7520       {
7521         can_clone = FALSE;
7522
7523         start_pos = check_pos[RND(8)];
7524         check_order = (RND(2) ? -1 : +1);
7525
7526         for (i = 0; i < 8; i++)
7527         {
7528           int pos_raw = start_pos + i * check_order;
7529           int pos = (pos_raw + 8) % 8;
7530           int newx = x + check_xy[pos].dx;
7531           int newy = y + check_xy[pos].dy;
7532
7533           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7534           {
7535             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7536             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7537
7538             Store[x][y] = Feld[newx][newy];
7539
7540             can_clone = TRUE;
7541
7542             break;
7543           }
7544         }
7545       }
7546
7547       if (can_clone)            /* randomly find a direction to move */
7548       {
7549         can_clone = FALSE;
7550
7551         start_pos = check_pos[RND(8)];
7552         check_order = (RND(2) ? -1 : +1);
7553
7554         for (i = 0; i < 8; i++)
7555         {
7556           int pos_raw = start_pos + i * check_order;
7557           int pos = (pos_raw + 8) % 8;
7558           int newx = x + check_xy[pos].dx;
7559           int newy = y + check_xy[pos].dy;
7560           int new_move_dir = check_xy[pos].dir;
7561
7562           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7563           {
7564             MovDir[x][y] = new_move_dir;
7565             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7566
7567             can_clone = TRUE;
7568
7569             break;
7570           }
7571         }
7572       }
7573
7574       if (can_clone)            /* cloning and moving successful */
7575         return;
7576
7577       /* cannot clone -- try to move towards player */
7578
7579       start_pos = check_pos[MovDir[x][y] & 0x0f];
7580       check_order = (RND(2) ? -1 : +1);
7581
7582       for (i = 0; i < 3; i++)
7583       {
7584         /* first check start_pos, then previous/next or (next/previous) pos */
7585         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7586         int pos = (pos_raw + 8) % 8;
7587         int newx = x + check_xy[pos].dx;
7588         int newy = y + check_xy[pos].dy;
7589         int new_move_dir = check_xy[pos].dir;
7590
7591         if (IS_PLAYER(newx, newy))
7592           break;
7593
7594         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7595         {
7596           MovDir[x][y] = new_move_dir;
7597           MovDelay[x][y] = level.android_move_time * 8 + 1;
7598
7599           break;
7600         }
7601       }
7602     }
7603   }
7604   else if (move_pattern == MV_TURNING_LEFT ||
7605            move_pattern == MV_TURNING_RIGHT ||
7606            move_pattern == MV_TURNING_LEFT_RIGHT ||
7607            move_pattern == MV_TURNING_RIGHT_LEFT ||
7608            move_pattern == MV_TURNING_RANDOM ||
7609            move_pattern == MV_ALL_DIRECTIONS)
7610   {
7611     boolean can_turn_left =
7612       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7613     boolean can_turn_right =
7614       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7615
7616     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7617       return;
7618
7619     if (move_pattern == MV_TURNING_LEFT)
7620       MovDir[x][y] = left_dir;
7621     else if (move_pattern == MV_TURNING_RIGHT)
7622       MovDir[x][y] = right_dir;
7623     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7624       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7625     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7626       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7627     else if (move_pattern == MV_TURNING_RANDOM)
7628       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7629                       can_turn_right && !can_turn_left ? right_dir :
7630                       RND(2) ? left_dir : right_dir);
7631     else if (can_turn_left && can_turn_right)
7632       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7633     else if (can_turn_left)
7634       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7635     else if (can_turn_right)
7636       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7637     else
7638       MovDir[x][y] = back_dir;
7639
7640     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7641   }
7642   else if (move_pattern == MV_HORIZONTAL ||
7643            move_pattern == MV_VERTICAL)
7644   {
7645     if (move_pattern & old_move_dir)
7646       MovDir[x][y] = back_dir;
7647     else if (move_pattern == MV_HORIZONTAL)
7648       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7649     else if (move_pattern == MV_VERTICAL)
7650       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7651
7652     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7653   }
7654   else if (move_pattern & MV_ANY_DIRECTION)
7655   {
7656     MovDir[x][y] = move_pattern;
7657     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7658   }
7659   else if (move_pattern & MV_WIND_DIRECTION)
7660   {
7661     MovDir[x][y] = game.wind_direction;
7662     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7663   }
7664   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7665   {
7666     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7667       MovDir[x][y] = left_dir;
7668     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7669       MovDir[x][y] = right_dir;
7670
7671     if (MovDir[x][y] != old_move_dir)
7672       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7673   }
7674   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7675   {
7676     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7677       MovDir[x][y] = right_dir;
7678     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7679       MovDir[x][y] = left_dir;
7680
7681     if (MovDir[x][y] != old_move_dir)
7682       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683   }
7684   else if (move_pattern == MV_TOWARDS_PLAYER ||
7685            move_pattern == MV_AWAY_FROM_PLAYER)
7686   {
7687     int attr_x = -1, attr_y = -1;
7688     int newx, newy;
7689     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7690
7691     if (AllPlayersGone)
7692     {
7693       attr_x = ExitX;
7694       attr_y = ExitY;
7695     }
7696     else
7697     {
7698       int i;
7699
7700       for (i = 0; i < MAX_PLAYERS; i++)
7701       {
7702         struct PlayerInfo *player = &stored_player[i];
7703         int jx = player->jx, jy = player->jy;
7704
7705         if (!player->active)
7706           continue;
7707
7708         if (attr_x == -1 ||
7709             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7710         {
7711           attr_x = jx;
7712           attr_y = jy;
7713         }
7714       }
7715     }
7716
7717     MovDir[x][y] = MV_NONE;
7718     if (attr_x < x)
7719       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7720     else if (attr_x > x)
7721       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7722     if (attr_y < y)
7723       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7724     else if (attr_y > y)
7725       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7726
7727     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7728
7729     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7730     {
7731       boolean first_horiz = RND(2);
7732       int new_move_dir = MovDir[x][y];
7733
7734       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7735       {
7736         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7737         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7738
7739         return;
7740       }
7741
7742       MovDir[x][y] =
7743         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744       Moving2Blocked(x, y, &newx, &newy);
7745
7746       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7747         return;
7748
7749       MovDir[x][y] =
7750         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7751       Moving2Blocked(x, y, &newx, &newy);
7752
7753       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7754         return;
7755
7756       MovDir[x][y] = old_move_dir;
7757     }
7758   }
7759   else if (move_pattern == MV_WHEN_PUSHED ||
7760            move_pattern == MV_WHEN_DROPPED)
7761   {
7762     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763       MovDir[x][y] = MV_NONE;
7764
7765     MovDelay[x][y] = 0;
7766   }
7767   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7768   {
7769     static int test_xy[7][2] =
7770     {
7771       { 0, -1 },
7772       { -1, 0 },
7773       { +1, 0 },
7774       { 0, +1 },
7775       { 0, -1 },
7776       { -1, 0 },
7777       { +1, 0 },
7778     };
7779     static int test_dir[7] =
7780     {
7781       MV_UP,
7782       MV_LEFT,
7783       MV_RIGHT,
7784       MV_DOWN,
7785       MV_UP,
7786       MV_LEFT,
7787       MV_RIGHT,
7788     };
7789     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7790     int move_preference = -1000000;     /* start with very low preference */
7791     int new_move_dir = MV_NONE;
7792     int start_test = RND(4);
7793     int i;
7794
7795     for (i = 0; i < NUM_DIRECTIONS; i++)
7796     {
7797       int move_dir = test_dir[start_test + i];
7798       int move_dir_preference;
7799
7800       xx = x + test_xy[start_test + i][0];
7801       yy = y + test_xy[start_test + i][1];
7802
7803       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7804           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7805       {
7806         new_move_dir = move_dir;
7807
7808         break;
7809       }
7810
7811       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7812         continue;
7813
7814       move_dir_preference = -1 * RunnerVisit[xx][yy];
7815       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7816         move_dir_preference = PlayerVisit[xx][yy];
7817
7818       if (move_dir_preference > move_preference)
7819       {
7820         /* prefer field that has not been visited for the longest time */
7821         move_preference = move_dir_preference;
7822         new_move_dir = move_dir;
7823       }
7824       else if (move_dir_preference == move_preference &&
7825                move_dir == old_move_dir)
7826       {
7827         /* prefer last direction when all directions are preferred equally */
7828         move_preference = move_dir_preference;
7829         new_move_dir = move_dir;
7830       }
7831     }
7832
7833     MovDir[x][y] = new_move_dir;
7834     if (old_move_dir != new_move_dir)
7835       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7836   }
7837 }
7838
7839 static void TurnRound(int x, int y)
7840 {
7841   int direction = MovDir[x][y];
7842
7843   TurnRoundExt(x, y);
7844
7845   GfxDir[x][y] = MovDir[x][y];
7846
7847   if (direction != MovDir[x][y])
7848     GfxFrame[x][y] = 0;
7849
7850   if (MovDelay[x][y])
7851     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7852
7853   ResetGfxFrame(x, y, FALSE);
7854 }
7855
7856 static boolean JustBeingPushed(int x, int y)
7857 {
7858   int i;
7859
7860   for (i = 0; i < MAX_PLAYERS; i++)
7861   {
7862     struct PlayerInfo *player = &stored_player[i];
7863
7864     if (player->active && player->is_pushing && player->MovPos)
7865     {
7866       int next_jx = player->jx + (player->jx - player->last_jx);
7867       int next_jy = player->jy + (player->jy - player->last_jy);
7868
7869       if (x == next_jx && y == next_jy)
7870         return TRUE;
7871     }
7872   }
7873
7874   return FALSE;
7875 }
7876
7877 void StartMoving(int x, int y)
7878 {
7879   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7880   int element = Feld[x][y];
7881
7882   if (Stop[x][y])
7883     return;
7884
7885   if (MovDelay[x][y] == 0)
7886     GfxAction[x][y] = ACTION_DEFAULT;
7887
7888   if (CAN_FALL(element) && y < lev_fieldy - 1)
7889   {
7890     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7891         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7892       if (JustBeingPushed(x, y))
7893         return;
7894
7895     if (element == EL_QUICKSAND_FULL)
7896     {
7897       if (IS_FREE(x, y + 1))
7898       {
7899         InitMovingField(x, y, MV_DOWN);
7900         started_moving = TRUE;
7901
7902         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7903 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7904         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7905           Store[x][y] = EL_ROCK;
7906 #else
7907         Store[x][y] = EL_ROCK;
7908 #endif
7909
7910         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7911       }
7912       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7913       {
7914         if (!MovDelay[x][y])
7915         {
7916           MovDelay[x][y] = TILEY + 1;
7917
7918           ResetGfxAnimation(x, y);
7919           ResetGfxAnimation(x, y + 1);
7920         }
7921
7922         if (MovDelay[x][y])
7923         {
7924           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7925           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7926
7927           MovDelay[x][y]--;
7928           if (MovDelay[x][y])
7929             return;
7930         }
7931
7932         Feld[x][y] = EL_QUICKSAND_EMPTY;
7933         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7934         Store[x][y + 1] = Store[x][y];
7935         Store[x][y] = 0;
7936
7937         PlayLevelSoundAction(x, y, ACTION_FILLING);
7938       }
7939       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7940       {
7941         if (!MovDelay[x][y])
7942         {
7943           MovDelay[x][y] = TILEY + 1;
7944
7945           ResetGfxAnimation(x, y);
7946           ResetGfxAnimation(x, y + 1);
7947         }
7948
7949         if (MovDelay[x][y])
7950         {
7951           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7952           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7953
7954           MovDelay[x][y]--;
7955           if (MovDelay[x][y])
7956             return;
7957         }
7958
7959         Feld[x][y] = EL_QUICKSAND_EMPTY;
7960         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7961         Store[x][y + 1] = Store[x][y];
7962         Store[x][y] = 0;
7963
7964         PlayLevelSoundAction(x, y, ACTION_FILLING);
7965       }
7966     }
7967     else if (element == EL_QUICKSAND_FAST_FULL)
7968     {
7969       if (IS_FREE(x, y + 1))
7970       {
7971         InitMovingField(x, y, MV_DOWN);
7972         started_moving = TRUE;
7973
7974         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7976         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7977           Store[x][y] = EL_ROCK;
7978 #else
7979         Store[x][y] = EL_ROCK;
7980 #endif
7981
7982         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7983       }
7984       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7985       {
7986         if (!MovDelay[x][y])
7987         {
7988           MovDelay[x][y] = TILEY + 1;
7989
7990           ResetGfxAnimation(x, y);
7991           ResetGfxAnimation(x, y + 1);
7992         }
7993
7994         if (MovDelay[x][y])
7995         {
7996           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7997           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7998
7999           MovDelay[x][y]--;
8000           if (MovDelay[x][y])
8001             return;
8002         }
8003
8004         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8005         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8006         Store[x][y + 1] = Store[x][y];
8007         Store[x][y] = 0;
8008
8009         PlayLevelSoundAction(x, y, ACTION_FILLING);
8010       }
8011       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8012       {
8013         if (!MovDelay[x][y])
8014         {
8015           MovDelay[x][y] = TILEY + 1;
8016
8017           ResetGfxAnimation(x, y);
8018           ResetGfxAnimation(x, y + 1);
8019         }
8020
8021         if (MovDelay[x][y])
8022         {
8023           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8024           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8025
8026           MovDelay[x][y]--;
8027           if (MovDelay[x][y])
8028             return;
8029         }
8030
8031         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8032         Feld[x][y + 1] = EL_QUICKSAND_FULL;
8033         Store[x][y + 1] = Store[x][y];
8034         Store[x][y] = 0;
8035
8036         PlayLevelSoundAction(x, y, ACTION_FILLING);
8037       }
8038     }
8039     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8040              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8041     {
8042       InitMovingField(x, y, MV_DOWN);
8043       started_moving = TRUE;
8044
8045       Feld[x][y] = EL_QUICKSAND_FILLING;
8046       Store[x][y] = element;
8047
8048       PlayLevelSoundAction(x, y, ACTION_FILLING);
8049     }
8050     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8051              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8052     {
8053       InitMovingField(x, y, MV_DOWN);
8054       started_moving = TRUE;
8055
8056       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8057       Store[x][y] = element;
8058
8059       PlayLevelSoundAction(x, y, ACTION_FILLING);
8060     }
8061     else if (element == EL_MAGIC_WALL_FULL)
8062     {
8063       if (IS_FREE(x, y + 1))
8064       {
8065         InitMovingField(x, y, MV_DOWN);
8066         started_moving = TRUE;
8067
8068         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8069         Store[x][y] = EL_CHANGED(Store[x][y]);
8070       }
8071       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8072       {
8073         if (!MovDelay[x][y])
8074           MovDelay[x][y] = TILEY/4 + 1;
8075
8076         if (MovDelay[x][y])
8077         {
8078           MovDelay[x][y]--;
8079           if (MovDelay[x][y])
8080             return;
8081         }
8082
8083         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8084         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8085         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8086         Store[x][y] = 0;
8087       }
8088     }
8089     else if (element == EL_BD_MAGIC_WALL_FULL)
8090     {
8091       if (IS_FREE(x, y + 1))
8092       {
8093         InitMovingField(x, y, MV_DOWN);
8094         started_moving = TRUE;
8095
8096         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8097         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8098       }
8099       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8100       {
8101         if (!MovDelay[x][y])
8102           MovDelay[x][y] = TILEY/4 + 1;
8103
8104         if (MovDelay[x][y])
8105         {
8106           MovDelay[x][y]--;
8107           if (MovDelay[x][y])
8108             return;
8109         }
8110
8111         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8112         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8113         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8114         Store[x][y] = 0;
8115       }
8116     }
8117     else if (element == EL_DC_MAGIC_WALL_FULL)
8118     {
8119       if (IS_FREE(x, y + 1))
8120       {
8121         InitMovingField(x, y, MV_DOWN);
8122         started_moving = TRUE;
8123
8124         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8125         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8126       }
8127       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8128       {
8129         if (!MovDelay[x][y])
8130           MovDelay[x][y] = TILEY/4 + 1;
8131
8132         if (MovDelay[x][y])
8133         {
8134           MovDelay[x][y]--;
8135           if (MovDelay[x][y])
8136             return;
8137         }
8138
8139         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8140         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8141         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8142         Store[x][y] = 0;
8143       }
8144     }
8145     else if ((CAN_PASS_MAGIC_WALL(element) &&
8146               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8147                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8148              (CAN_PASS_DC_MAGIC_WALL(element) &&
8149               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8150
8151     {
8152       InitMovingField(x, y, MV_DOWN);
8153       started_moving = TRUE;
8154
8155       Feld[x][y] =
8156         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8157          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8158          EL_DC_MAGIC_WALL_FILLING);
8159       Store[x][y] = element;
8160     }
8161     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8162     {
8163       SplashAcid(x, y + 1);
8164
8165       InitMovingField(x, y, MV_DOWN);
8166       started_moving = TRUE;
8167
8168       Store[x][y] = EL_ACID;
8169     }
8170     else if (
8171 #if USE_FIX_IMPACT_COLLISION
8172              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8173               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8174 #else
8175              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8176               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8177 #endif
8178              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8179               CAN_FALL(element) && WasJustFalling[x][y] &&
8180               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8181
8182              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8183               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8184               (Feld[x][y + 1] == EL_BLOCKED)))
8185     {
8186       /* this is needed for a special case not covered by calling "Impact()"
8187          from "ContinueMoving()": if an element moves to a tile directly below
8188          another element which was just falling on that tile (which was empty
8189          in the previous frame), the falling element above would just stop
8190          instead of smashing the element below (in previous version, the above
8191          element was just checked for "moving" instead of "falling", resulting
8192          in incorrect smashes caused by horizontal movement of the above
8193          element; also, the case of the player being the element to smash was
8194          simply not covered here... :-/ ) */
8195
8196       CheckCollision[x][y] = 0;
8197       CheckImpact[x][y] = 0;
8198
8199       Impact(x, y);
8200     }
8201     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8202     {
8203       if (MovDir[x][y] == MV_NONE)
8204       {
8205         InitMovingField(x, y, MV_DOWN);
8206         started_moving = TRUE;
8207       }
8208     }
8209     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8210     {
8211       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8212         MovDir[x][y] = MV_DOWN;
8213
8214       InitMovingField(x, y, MV_DOWN);
8215       started_moving = TRUE;
8216     }
8217     else if (element == EL_AMOEBA_DROP)
8218     {
8219       Feld[x][y] = EL_AMOEBA_GROWING;
8220       Store[x][y] = EL_AMOEBA_WET;
8221     }
8222     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8223               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8224              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8225              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8226     {
8227       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8228                                 (IS_FREE(x - 1, y + 1) ||
8229                                  Feld[x - 1][y + 1] == EL_ACID));
8230       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8231                                 (IS_FREE(x + 1, y + 1) ||
8232                                  Feld[x + 1][y + 1] == EL_ACID));
8233       boolean can_fall_any  = (can_fall_left || can_fall_right);
8234       boolean can_fall_both = (can_fall_left && can_fall_right);
8235       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8236
8237 #if USE_NEW_ALL_SLIPPERY
8238       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8239       {
8240         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8241           can_fall_right = FALSE;
8242         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8243           can_fall_left = FALSE;
8244         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8245           can_fall_right = FALSE;
8246         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8247           can_fall_left = FALSE;
8248
8249         can_fall_any  = (can_fall_left || can_fall_right);
8250         can_fall_both = FALSE;
8251       }
8252 #else
8253       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8254       {
8255         if (slippery_type == SLIPPERY_ONLY_LEFT)
8256           can_fall_right = FALSE;
8257         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8258           can_fall_left = FALSE;
8259         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8260           can_fall_right = FALSE;
8261         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8262           can_fall_left = FALSE;
8263
8264         can_fall_any  = (can_fall_left || can_fall_right);
8265         can_fall_both = (can_fall_left && can_fall_right);
8266       }
8267 #endif
8268
8269 #if USE_NEW_ALL_SLIPPERY
8270 #else
8271 #if USE_NEW_SP_SLIPPERY
8272       /* !!! better use the same properties as for custom elements here !!! */
8273       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8274                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8275       {
8276         can_fall_right = FALSE;         /* slip down on left side */
8277         can_fall_both = FALSE;
8278       }
8279 #endif
8280 #endif
8281
8282 #if USE_NEW_ALL_SLIPPERY
8283       if (can_fall_both)
8284       {
8285         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8286           can_fall_right = FALSE;       /* slip down on left side */
8287         else
8288           can_fall_left = !(can_fall_right = RND(2));
8289
8290         can_fall_both = FALSE;
8291       }
8292 #else
8293       if (can_fall_both)
8294       {
8295         if (game.emulation == EMU_BOULDERDASH ||
8296             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8297           can_fall_right = FALSE;       /* slip down on left side */
8298         else
8299           can_fall_left = !(can_fall_right = RND(2));
8300
8301         can_fall_both = FALSE;
8302       }
8303 #endif
8304
8305       if (can_fall_any)
8306       {
8307         /* if not determined otherwise, prefer left side for slipping down */
8308         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8309         started_moving = TRUE;
8310       }
8311     }
8312 #if 0
8313     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8314 #else
8315     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8316 #endif
8317     {
8318       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8319       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8320       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8321       int belt_dir = game.belt_dir[belt_nr];
8322
8323       if ((belt_dir == MV_LEFT  && left_is_free) ||
8324           (belt_dir == MV_RIGHT && right_is_free))
8325       {
8326         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8327
8328         InitMovingField(x, y, belt_dir);
8329         started_moving = TRUE;
8330
8331         Pushed[x][y] = TRUE;
8332         Pushed[nextx][y] = TRUE;
8333
8334         GfxAction[x][y] = ACTION_DEFAULT;
8335       }
8336       else
8337       {
8338         MovDir[x][y] = 0;       /* if element was moving, stop it */
8339       }
8340     }
8341   }
8342
8343   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8344 #if 0
8345   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8346 #else
8347   if (CAN_MOVE(element) && !started_moving)
8348 #endif
8349   {
8350     int move_pattern = element_info[element].move_pattern;
8351     int newx, newy;
8352
8353 #if 0
8354 #if DEBUG
8355     if (MovDir[x][y] == MV_NONE)
8356     {
8357       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8358              x, y, element, element_info[element].token_name);
8359       printf("StartMoving(): This should never happen!\n");
8360     }
8361 #endif
8362 #endif
8363
8364     Moving2Blocked(x, y, &newx, &newy);
8365
8366     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8367       return;
8368
8369     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8370         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8371     {
8372       WasJustMoving[x][y] = 0;
8373       CheckCollision[x][y] = 0;
8374
8375       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8376
8377       if (Feld[x][y] != element)        /* element has changed */
8378         return;
8379     }
8380
8381     if (!MovDelay[x][y])        /* start new movement phase */
8382     {
8383       /* all objects that can change their move direction after each step
8384          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8385
8386       if (element != EL_YAMYAM &&
8387           element != EL_DARK_YAMYAM &&
8388           element != EL_PACMAN &&
8389           !(move_pattern & MV_ANY_DIRECTION) &&
8390           move_pattern != MV_TURNING_LEFT &&
8391           move_pattern != MV_TURNING_RIGHT &&
8392           move_pattern != MV_TURNING_LEFT_RIGHT &&
8393           move_pattern != MV_TURNING_RIGHT_LEFT &&
8394           move_pattern != MV_TURNING_RANDOM)
8395       {
8396         TurnRound(x, y);
8397
8398         if (MovDelay[x][y] && (element == EL_BUG ||
8399                                element == EL_SPACESHIP ||
8400                                element == EL_SP_SNIKSNAK ||
8401                                element == EL_SP_ELECTRON ||
8402                                element == EL_MOLE))
8403           TEST_DrawLevelField(x, y);
8404       }
8405     }
8406
8407     if (MovDelay[x][y])         /* wait some time before next movement */
8408     {
8409       MovDelay[x][y]--;
8410
8411       if (element == EL_ROBOT ||
8412           element == EL_YAMYAM ||
8413           element == EL_DARK_YAMYAM)
8414       {
8415         DrawLevelElementAnimationIfNeeded(x, y, element);
8416         PlayLevelSoundAction(x, y, ACTION_WAITING);
8417       }
8418       else if (element == EL_SP_ELECTRON)
8419         DrawLevelElementAnimationIfNeeded(x, y, element);
8420       else if (element == EL_DRAGON)
8421       {
8422         int i;
8423         int dir = MovDir[x][y];
8424         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8425         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8426         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8427                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8428                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8429                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8430         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8431
8432         GfxAction[x][y] = ACTION_ATTACKING;
8433
8434         if (IS_PLAYER(x, y))
8435           DrawPlayerField(x, y);
8436         else
8437           TEST_DrawLevelField(x, y);
8438
8439         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8440
8441         for (i = 1; i <= 3; i++)
8442         {
8443           int xx = x + i * dx;
8444           int yy = y + i * dy;
8445           int sx = SCREENX(xx);
8446           int sy = SCREENY(yy);
8447           int flame_graphic = graphic + (i - 1);
8448
8449           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8450             break;
8451
8452           if (MovDelay[x][y])
8453           {
8454             int flamed = MovingOrBlocked2Element(xx, yy);
8455
8456             /* !!! */
8457 #if 0
8458             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8459               Bang(xx, yy);
8460             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8461               RemoveMovingField(xx, yy);
8462             else
8463               RemoveField(xx, yy);
8464 #else
8465             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8466               Bang(xx, yy);
8467             else
8468               RemoveMovingField(xx, yy);
8469 #endif
8470
8471             ChangeDelay[xx][yy] = 0;
8472
8473             Feld[xx][yy] = EL_FLAMES;
8474
8475             if (IN_SCR_FIELD(sx, sy))
8476             {
8477               TEST_DrawLevelFieldCrumbledSand(xx, yy);
8478               DrawGraphic(sx, sy, flame_graphic, frame);
8479             }
8480           }
8481           else
8482           {
8483             if (Feld[xx][yy] == EL_FLAMES)
8484               Feld[xx][yy] = EL_EMPTY;
8485             TEST_DrawLevelField(xx, yy);
8486           }
8487         }
8488       }
8489
8490       if (MovDelay[x][y])       /* element still has to wait some time */
8491       {
8492         PlayLevelSoundAction(x, y, ACTION_WAITING);
8493
8494         return;
8495       }
8496     }
8497
8498     /* now make next step */
8499
8500     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8501
8502     if (DONT_COLLIDE_WITH(element) &&
8503         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8504         !PLAYER_ENEMY_PROTECTED(newx, newy))
8505     {
8506       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8507
8508       return;
8509     }
8510
8511     else if (CAN_MOVE_INTO_ACID(element) &&
8512              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8513              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8514              (MovDir[x][y] == MV_DOWN ||
8515               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8516     {
8517       SplashAcid(newx, newy);
8518       Store[x][y] = EL_ACID;
8519     }
8520     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8521     {
8522       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8523           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8524           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8525           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8526       {
8527         RemoveField(x, y);
8528         TEST_DrawLevelField(x, y);
8529
8530         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8531         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8532           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8533
8534         local_player->friends_still_needed--;
8535         if (!local_player->friends_still_needed &&
8536             !local_player->GameOver && AllPlayersGone)
8537           PlayerWins(local_player);
8538
8539         return;
8540       }
8541       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8542       {
8543         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8544           TEST_DrawLevelField(newx, newy);
8545         else
8546           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8547       }
8548       else if (!IS_FREE(newx, newy))
8549       {
8550         GfxAction[x][y] = ACTION_WAITING;
8551
8552         if (IS_PLAYER(x, y))
8553           DrawPlayerField(x, y);
8554         else
8555           TEST_DrawLevelField(x, y);
8556
8557         return;
8558       }
8559     }
8560     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8561     {
8562       if (IS_FOOD_PIG(Feld[newx][newy]))
8563       {
8564         if (IS_MOVING(newx, newy))
8565           RemoveMovingField(newx, newy);
8566         else
8567         {
8568           Feld[newx][newy] = EL_EMPTY;
8569           TEST_DrawLevelField(newx, newy);
8570         }
8571
8572         PlayLevelSound(x, y, SND_PIG_DIGGING);
8573       }
8574       else if (!IS_FREE(newx, newy))
8575       {
8576         if (IS_PLAYER(x, y))
8577           DrawPlayerField(x, y);
8578         else
8579           TEST_DrawLevelField(x, y);
8580
8581         return;
8582       }
8583     }
8584     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8585     {
8586       if (Store[x][y] != EL_EMPTY)
8587       {
8588         boolean can_clone = FALSE;
8589         int xx, yy;
8590
8591         /* check if element to clone is still there */
8592         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8593         {
8594           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8595           {
8596             can_clone = TRUE;
8597
8598             break;
8599           }
8600         }
8601
8602         /* cannot clone or target field not free anymore -- do not clone */
8603         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8604           Store[x][y] = EL_EMPTY;
8605       }
8606
8607       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8608       {
8609         if (IS_MV_DIAGONAL(MovDir[x][y]))
8610         {
8611           int diagonal_move_dir = MovDir[x][y];
8612           int stored = Store[x][y];
8613           int change_delay = 8;
8614           int graphic;
8615
8616           /* android is moving diagonally */
8617
8618           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8619
8620           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8621           GfxElement[x][y] = EL_EMC_ANDROID;
8622           GfxAction[x][y] = ACTION_SHRINKING;
8623           GfxDir[x][y] = diagonal_move_dir;
8624           ChangeDelay[x][y] = change_delay;
8625
8626           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8627                                    GfxDir[x][y]);
8628
8629           DrawLevelGraphicAnimation(x, y, graphic);
8630           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8631
8632           if (Feld[newx][newy] == EL_ACID)
8633           {
8634             SplashAcid(newx, newy);
8635
8636             return;
8637           }
8638
8639           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8640
8641           Store[newx][newy] = EL_EMC_ANDROID;
8642           GfxElement[newx][newy] = EL_EMC_ANDROID;
8643           GfxAction[newx][newy] = ACTION_GROWING;
8644           GfxDir[newx][newy] = diagonal_move_dir;
8645           ChangeDelay[newx][newy] = change_delay;
8646
8647           graphic = el_act_dir2img(GfxElement[newx][newy],
8648                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8649
8650           DrawLevelGraphicAnimation(newx, newy, graphic);
8651           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8652
8653           return;
8654         }
8655         else
8656         {
8657           Feld[newx][newy] = EL_EMPTY;
8658           TEST_DrawLevelField(newx, newy);
8659
8660           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8661         }
8662       }
8663       else if (!IS_FREE(newx, newy))
8664       {
8665 #if 0
8666         if (IS_PLAYER(x, y))
8667           DrawPlayerField(x, y);
8668         else
8669           TEST_DrawLevelField(x, y);
8670 #endif
8671
8672         return;
8673       }
8674     }
8675     else if (IS_CUSTOM_ELEMENT(element) &&
8676              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8677     {
8678 #if 1
8679       if (!DigFieldByCE(newx, newy, element))
8680         return;
8681 #else
8682       int new_element = Feld[newx][newy];
8683
8684       if (!IS_FREE(newx, newy))
8685       {
8686         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8687                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8688                       ACTION_BREAKING);
8689
8690         /* no element can dig solid indestructible elements */
8691         if (IS_INDESTRUCTIBLE(new_element) &&
8692             !IS_DIGGABLE(new_element) &&
8693             !IS_COLLECTIBLE(new_element))
8694           return;
8695
8696         if (AmoebaNr[newx][newy] &&
8697             (new_element == EL_AMOEBA_FULL ||
8698              new_element == EL_BD_AMOEBA ||
8699              new_element == EL_AMOEBA_GROWING))
8700         {
8701           AmoebaCnt[AmoebaNr[newx][newy]]--;
8702           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8703         }
8704
8705         if (IS_MOVING(newx, newy))
8706           RemoveMovingField(newx, newy);
8707         else
8708         {
8709           RemoveField(newx, newy);
8710           TEST_DrawLevelField(newx, newy);
8711         }
8712
8713         /* if digged element was about to explode, prevent the explosion */
8714         ExplodeField[newx][newy] = EX_TYPE_NONE;
8715
8716         PlayLevelSoundAction(x, y, action);
8717       }
8718
8719       Store[newx][newy] = EL_EMPTY;
8720
8721 #if 1
8722       /* this makes it possible to leave the removed element again */
8723       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8724         Store[newx][newy] = new_element;
8725 #else
8726       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8727       {
8728         int move_leave_element = element_info[element].move_leave_element;
8729
8730         /* this makes it possible to leave the removed element again */
8731         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8732                              new_element : move_leave_element);
8733       }
8734 #endif
8735
8736 #endif
8737
8738       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8739       {
8740         RunnerVisit[x][y] = FrameCounter;
8741         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8742       }
8743     }
8744     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8745     {
8746       if (!IS_FREE(newx, newy))
8747       {
8748         if (IS_PLAYER(x, y))
8749           DrawPlayerField(x, y);
8750         else
8751           TEST_DrawLevelField(x, y);
8752
8753         return;
8754       }
8755       else
8756       {
8757         boolean wanna_flame = !RND(10);
8758         int dx = newx - x, dy = newy - y;
8759         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8760         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8761         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8762                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8763         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8764                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8765
8766         if ((wanna_flame ||
8767              IS_CLASSIC_ENEMY(element1) ||
8768              IS_CLASSIC_ENEMY(element2)) &&
8769             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8770             element1 != EL_FLAMES && element2 != EL_FLAMES)
8771         {
8772           ResetGfxAnimation(x, y);
8773           GfxAction[x][y] = ACTION_ATTACKING;
8774
8775           if (IS_PLAYER(x, y))
8776             DrawPlayerField(x, y);
8777           else
8778             TEST_DrawLevelField(x, y);
8779
8780           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8781
8782           MovDelay[x][y] = 50;
8783
8784           /* !!! */
8785 #if 0
8786           RemoveField(newx, newy);
8787 #endif
8788           Feld[newx][newy] = EL_FLAMES;
8789           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8790           {
8791 #if 0
8792             RemoveField(newx1, newy1);
8793 #endif
8794             Feld[newx1][newy1] = EL_FLAMES;
8795           }
8796           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8797           {
8798 #if 0
8799             RemoveField(newx2, newy2);
8800 #endif
8801             Feld[newx2][newy2] = EL_FLAMES;
8802           }
8803
8804           return;
8805         }
8806       }
8807     }
8808     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8809              Feld[newx][newy] == EL_DIAMOND)
8810     {
8811       if (IS_MOVING(newx, newy))
8812         RemoveMovingField(newx, newy);
8813       else
8814       {
8815         Feld[newx][newy] = EL_EMPTY;
8816         TEST_DrawLevelField(newx, newy);
8817       }
8818
8819       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8820     }
8821     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8822              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8823     {
8824       if (AmoebaNr[newx][newy])
8825       {
8826         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8827         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8828             Feld[newx][newy] == EL_BD_AMOEBA)
8829           AmoebaCnt[AmoebaNr[newx][newy]]--;
8830       }
8831
8832 #if 0
8833       /* !!! test !!! */
8834       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8835       {
8836         RemoveMovingField(newx, newy);
8837       }
8838 #else
8839       if (IS_MOVING(newx, newy))
8840       {
8841         RemoveMovingField(newx, newy);
8842       }
8843 #endif
8844       else
8845       {
8846         Feld[newx][newy] = EL_EMPTY;
8847         TEST_DrawLevelField(newx, newy);
8848       }
8849
8850       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8851     }
8852     else if ((element == EL_PACMAN || element == EL_MOLE)
8853              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8854     {
8855       if (AmoebaNr[newx][newy])
8856       {
8857         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8858         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8859             Feld[newx][newy] == EL_BD_AMOEBA)
8860           AmoebaCnt[AmoebaNr[newx][newy]]--;
8861       }
8862
8863       if (element == EL_MOLE)
8864       {
8865         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8866         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8867
8868         ResetGfxAnimation(x, y);
8869         GfxAction[x][y] = ACTION_DIGGING;
8870         TEST_DrawLevelField(x, y);
8871
8872         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8873
8874         return;                         /* wait for shrinking amoeba */
8875       }
8876       else      /* element == EL_PACMAN */
8877       {
8878         Feld[newx][newy] = EL_EMPTY;
8879         TEST_DrawLevelField(newx, newy);
8880         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8881       }
8882     }
8883     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8884              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8885               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8886     {
8887       /* wait for shrinking amoeba to completely disappear */
8888       return;
8889     }
8890     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8891     {
8892       /* object was running against a wall */
8893
8894       TurnRound(x, y);
8895
8896 #if 0
8897       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8898       if (move_pattern & MV_ANY_DIRECTION &&
8899           move_pattern == MovDir[x][y])
8900       {
8901         int blocking_element =
8902           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8903
8904         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8905                                  MovDir[x][y]);
8906
8907         element = Feld[x][y];   /* element might have changed */
8908       }
8909 #endif
8910
8911       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8912         DrawLevelElementAnimation(x, y, element);
8913
8914       if (DONT_TOUCH(element))
8915         TestIfBadThingTouchesPlayer(x, y);
8916
8917       return;
8918     }
8919
8920     InitMovingField(x, y, MovDir[x][y]);
8921
8922     PlayLevelSoundAction(x, y, ACTION_MOVING);
8923   }
8924
8925   if (MovDir[x][y])
8926     ContinueMoving(x, y);
8927 }
8928
8929 void ContinueMoving(int x, int y)
8930 {
8931   int element = Feld[x][y];
8932   struct ElementInfo *ei = &element_info[element];
8933   int direction = MovDir[x][y];
8934   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8935   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8936   int newx = x + dx, newy = y + dy;
8937   int stored = Store[x][y];
8938   int stored_new = Store[newx][newy];
8939   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8940   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8941   boolean last_line = (newy == lev_fieldy - 1);
8942
8943   MovPos[x][y] += getElementMoveStepsize(x, y);
8944
8945   if (pushed_by_player) /* special case: moving object pushed by player */
8946     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8947
8948   if (ABS(MovPos[x][y]) < TILEX)
8949   {
8950 #if 0
8951     int ee = Feld[x][y];
8952     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8953     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8954
8955     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8956            x, y, ABS(MovPos[x][y]),
8957            ee, gg, ff,
8958            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8959 #endif
8960
8961     TEST_DrawLevelField(x, y);
8962
8963     return;     /* element is still moving */
8964   }
8965
8966   /* element reached destination field */
8967
8968   Feld[x][y] = EL_EMPTY;
8969   Feld[newx][newy] = element;
8970   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8971
8972   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8973   {
8974     element = Feld[newx][newy] = EL_ACID;
8975   }
8976   else if (element == EL_MOLE)
8977   {
8978     Feld[x][y] = EL_SAND;
8979
8980     TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8981   }
8982   else if (element == EL_QUICKSAND_FILLING)
8983   {
8984     element = Feld[newx][newy] = get_next_element(element);
8985     Store[newx][newy] = Store[x][y];
8986   }
8987   else if (element == EL_QUICKSAND_EMPTYING)
8988   {
8989     Feld[x][y] = get_next_element(element);
8990     element = Feld[newx][newy] = Store[x][y];
8991   }
8992   else if (element == EL_QUICKSAND_FAST_FILLING)
8993   {
8994     element = Feld[newx][newy] = get_next_element(element);
8995     Store[newx][newy] = Store[x][y];
8996   }
8997   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8998   {
8999     Feld[x][y] = get_next_element(element);
9000     element = Feld[newx][newy] = Store[x][y];
9001   }
9002   else if (element == EL_MAGIC_WALL_FILLING)
9003   {
9004     element = Feld[newx][newy] = get_next_element(element);
9005     if (!game.magic_wall_active)
9006       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9007     Store[newx][newy] = Store[x][y];
9008   }
9009   else if (element == EL_MAGIC_WALL_EMPTYING)
9010   {
9011     Feld[x][y] = get_next_element(element);
9012     if (!game.magic_wall_active)
9013       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9014     element = Feld[newx][newy] = Store[x][y];
9015
9016 #if USE_NEW_CUSTOM_VALUE
9017     InitField(newx, newy, FALSE);
9018 #endif
9019   }
9020   else if (element == EL_BD_MAGIC_WALL_FILLING)
9021   {
9022     element = Feld[newx][newy] = get_next_element(element);
9023     if (!game.magic_wall_active)
9024       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9025     Store[newx][newy] = Store[x][y];
9026   }
9027   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9028   {
9029     Feld[x][y] = get_next_element(element);
9030     if (!game.magic_wall_active)
9031       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9032     element = Feld[newx][newy] = Store[x][y];
9033
9034 #if USE_NEW_CUSTOM_VALUE
9035     InitField(newx, newy, FALSE);
9036 #endif
9037   }
9038   else if (element == EL_DC_MAGIC_WALL_FILLING)
9039   {
9040     element = Feld[newx][newy] = get_next_element(element);
9041     if (!game.magic_wall_active)
9042       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9043     Store[newx][newy] = Store[x][y];
9044   }
9045   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9046   {
9047     Feld[x][y] = get_next_element(element);
9048     if (!game.magic_wall_active)
9049       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9050     element = Feld[newx][newy] = Store[x][y];
9051
9052 #if USE_NEW_CUSTOM_VALUE
9053     InitField(newx, newy, FALSE);
9054 #endif
9055   }
9056   else if (element == EL_AMOEBA_DROPPING)
9057   {
9058     Feld[x][y] = get_next_element(element);
9059     element = Feld[newx][newy] = Store[x][y];
9060   }
9061   else if (element == EL_SOKOBAN_OBJECT)
9062   {
9063     if (Back[x][y])
9064       Feld[x][y] = Back[x][y];
9065
9066     if (Back[newx][newy])
9067       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9068
9069     Back[x][y] = Back[newx][newy] = 0;
9070   }
9071
9072   Store[x][y] = EL_EMPTY;
9073   MovPos[x][y] = 0;
9074   MovDir[x][y] = 0;
9075   MovDelay[x][y] = 0;
9076
9077   MovDelay[newx][newy] = 0;
9078
9079   if (CAN_CHANGE_OR_HAS_ACTION(element))
9080   {
9081     /* copy element change control values to new field */
9082     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9083     ChangePage[newx][newy]  = ChangePage[x][y];
9084     ChangeCount[newx][newy] = ChangeCount[x][y];
9085     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9086   }
9087
9088 #if USE_NEW_CUSTOM_VALUE
9089   CustomValue[newx][newy] = CustomValue[x][y];
9090 #endif
9091
9092   ChangeDelay[x][y] = 0;
9093   ChangePage[x][y] = -1;
9094   ChangeCount[x][y] = 0;
9095   ChangeEvent[x][y] = -1;
9096
9097 #if USE_NEW_CUSTOM_VALUE
9098   CustomValue[x][y] = 0;
9099 #endif
9100
9101   /* copy animation control values to new field */
9102   GfxFrame[newx][newy]  = GfxFrame[x][y];
9103   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
9104   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
9105   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
9106
9107   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9108
9109   /* some elements can leave other elements behind after moving */
9110 #if 1
9111   if (ei->move_leave_element != EL_EMPTY &&
9112       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9113       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9114 #else
9115   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9116       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9117       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9118 #endif
9119   {
9120     int move_leave_element = ei->move_leave_element;
9121
9122 #if 1
9123 #if 1
9124     /* this makes it possible to leave the removed element again */
9125     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9126       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9127 #else
9128     /* this makes it possible to leave the removed element again */
9129     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9130       move_leave_element = stored;
9131 #endif
9132 #else
9133     /* this makes it possible to leave the removed element again */
9134     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9135         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9136       move_leave_element = stored;
9137 #endif
9138
9139     Feld[x][y] = move_leave_element;
9140
9141     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9142       MovDir[x][y] = direction;
9143
9144     InitField(x, y, FALSE);
9145
9146     if (GFX_CRUMBLED(Feld[x][y]))
9147       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9148
9149     if (ELEM_IS_PLAYER(move_leave_element))
9150       RelocatePlayer(x, y, move_leave_element);
9151   }
9152
9153   /* do this after checking for left-behind element */
9154   ResetGfxAnimation(x, y);      /* reset animation values for old field */
9155
9156   if (!CAN_MOVE(element) ||
9157       (CAN_FALL(element) && direction == MV_DOWN &&
9158        (element == EL_SPRING ||
9159         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9160         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9161     GfxDir[x][y] = MovDir[newx][newy] = 0;
9162
9163   TEST_DrawLevelField(x, y);
9164   TEST_DrawLevelField(newx, newy);
9165
9166   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
9167
9168   /* prevent pushed element from moving on in pushed direction */
9169   if (pushed_by_player && CAN_MOVE(element) &&
9170       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9171       !(element_info[element].move_pattern & direction))
9172     TurnRound(newx, newy);
9173
9174   /* prevent elements on conveyor belt from moving on in last direction */
9175   if (pushed_by_conveyor && CAN_FALL(element) &&
9176       direction & MV_HORIZONTAL)
9177     MovDir[newx][newy] = 0;
9178
9179   if (!pushed_by_player)
9180   {
9181     int nextx = newx + dx, nexty = newy + dy;
9182     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9183
9184     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9185
9186     if (CAN_FALL(element) && direction == MV_DOWN)
9187       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9188
9189     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9190       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9191
9192 #if USE_FIX_IMPACT_COLLISION
9193     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9194       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9195 #endif
9196   }
9197
9198   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
9199   {
9200     TestIfBadThingTouchesPlayer(newx, newy);
9201     TestIfBadThingTouchesFriend(newx, newy);
9202
9203     if (!IS_CUSTOM_ELEMENT(element))
9204       TestIfBadThingTouchesOtherBadThing(newx, newy);
9205   }
9206   else if (element == EL_PENGUIN)
9207     TestIfFriendTouchesBadThing(newx, newy);
9208
9209   if (DONT_GET_HIT_BY(element))
9210   {
9211     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9212   }
9213
9214   /* give the player one last chance (one more frame) to move away */
9215   if (CAN_FALL(element) && direction == MV_DOWN &&
9216       (last_line || (!IS_FREE(x, newy + 1) &&
9217                      (!IS_PLAYER(x, newy + 1) ||
9218                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9219     Impact(x, newy);
9220
9221   if (pushed_by_player && !game.use_change_when_pushing_bug)
9222   {
9223     int push_side = MV_DIR_OPPOSITE(direction);
9224     struct PlayerInfo *player = PLAYERINFO(x, y);
9225
9226     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9227                                player->index_bit, push_side);
9228     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9229                                         player->index_bit, push_side);
9230   }
9231
9232   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
9233     MovDelay[newx][newy] = 1;
9234
9235   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9236
9237   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
9238
9239 #if 0
9240   if (ChangePage[newx][newy] != -1)             /* delayed change */
9241   {
9242     int page = ChangePage[newx][newy];
9243     struct ElementChangeInfo *change = &ei->change_page[page];
9244
9245     ChangePage[newx][newy] = -1;
9246
9247     if (change->can_change)
9248     {
9249       if (ChangeElement(newx, newy, element, page))
9250       {
9251         if (change->post_change_function)
9252           change->post_change_function(newx, newy);
9253       }
9254     }
9255
9256     if (change->has_action)
9257       ExecuteCustomElementAction(newx, newy, element, page);
9258   }
9259 #endif
9260
9261   TestIfElementHitsCustomElement(newx, newy, direction);
9262   TestIfPlayerTouchesCustomElement(newx, newy);
9263   TestIfElementTouchesCustomElement(newx, newy);
9264
9265   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9266       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9267     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9268                              MV_DIR_OPPOSITE(direction));
9269 }
9270
9271 int AmoebeNachbarNr(int ax, int ay)
9272 {
9273   int i;
9274   int element = Feld[ax][ay];
9275   int group_nr = 0;
9276   static int xy[4][2] =
9277   {
9278     { 0, -1 },
9279     { -1, 0 },
9280     { +1, 0 },
9281     { 0, +1 }
9282   };
9283
9284   for (i = 0; i < NUM_DIRECTIONS; i++)
9285   {
9286     int x = ax + xy[i][0];
9287     int y = ay + xy[i][1];
9288
9289     if (!IN_LEV_FIELD(x, y))
9290       continue;
9291
9292     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9293       group_nr = AmoebaNr[x][y];
9294   }
9295
9296   return group_nr;
9297 }
9298
9299 void AmoebenVereinigen(int ax, int ay)
9300 {
9301   int i, x, y, xx, yy;
9302   int new_group_nr = AmoebaNr[ax][ay];
9303   static int xy[4][2] =
9304   {
9305     { 0, -1 },
9306     { -1, 0 },
9307     { +1, 0 },
9308     { 0, +1 }
9309   };
9310
9311   if (new_group_nr == 0)
9312     return;
9313
9314   for (i = 0; i < NUM_DIRECTIONS; i++)
9315   {
9316     x = ax + xy[i][0];
9317     y = ay + xy[i][1];
9318
9319     if (!IN_LEV_FIELD(x, y))
9320       continue;
9321
9322     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9323          Feld[x][y] == EL_BD_AMOEBA ||
9324          Feld[x][y] == EL_AMOEBA_DEAD) &&
9325         AmoebaNr[x][y] != new_group_nr)
9326     {
9327       int old_group_nr = AmoebaNr[x][y];
9328
9329       if (old_group_nr == 0)
9330         return;
9331
9332       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9333       AmoebaCnt[old_group_nr] = 0;
9334       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9335       AmoebaCnt2[old_group_nr] = 0;
9336
9337       SCAN_PLAYFIELD(xx, yy)
9338       {
9339         if (AmoebaNr[xx][yy] == old_group_nr)
9340           AmoebaNr[xx][yy] = new_group_nr;
9341       }
9342     }
9343   }
9344 }
9345
9346 void AmoebeUmwandeln(int ax, int ay)
9347 {
9348   int i, x, y;
9349
9350   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9351   {
9352     int group_nr = AmoebaNr[ax][ay];
9353
9354 #ifdef DEBUG
9355     if (group_nr == 0)
9356     {
9357       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9358       printf("AmoebeUmwandeln(): This should never happen!\n");
9359       return;
9360     }
9361 #endif
9362
9363     SCAN_PLAYFIELD(x, y)
9364     {
9365       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9366       {
9367         AmoebaNr[x][y] = 0;
9368         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9369       }
9370     }
9371
9372     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9373                             SND_AMOEBA_TURNING_TO_GEM :
9374                             SND_AMOEBA_TURNING_TO_ROCK));
9375     Bang(ax, ay);
9376   }
9377   else
9378   {
9379     static int xy[4][2] =
9380     {
9381       { 0, -1 },
9382       { -1, 0 },
9383       { +1, 0 },
9384       { 0, +1 }
9385     };
9386
9387     for (i = 0; i < NUM_DIRECTIONS; i++)
9388     {
9389       x = ax + xy[i][0];
9390       y = ay + xy[i][1];
9391
9392       if (!IN_LEV_FIELD(x, y))
9393         continue;
9394
9395       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9396       {
9397         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9398                               SND_AMOEBA_TURNING_TO_GEM :
9399                               SND_AMOEBA_TURNING_TO_ROCK));
9400         Bang(x, y);
9401       }
9402     }
9403   }
9404 }
9405
9406 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9407 {
9408   int x, y;
9409   int group_nr = AmoebaNr[ax][ay];
9410   boolean done = FALSE;
9411
9412 #ifdef DEBUG
9413   if (group_nr == 0)
9414   {
9415     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9416     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9417     return;
9418   }
9419 #endif
9420
9421   SCAN_PLAYFIELD(x, y)
9422   {
9423     if (AmoebaNr[x][y] == group_nr &&
9424         (Feld[x][y] == EL_AMOEBA_DEAD ||
9425          Feld[x][y] == EL_BD_AMOEBA ||
9426          Feld[x][y] == EL_AMOEBA_GROWING))
9427     {
9428       AmoebaNr[x][y] = 0;
9429       Feld[x][y] = new_element;
9430       InitField(x, y, FALSE);
9431       TEST_DrawLevelField(x, y);
9432       done = TRUE;
9433     }
9434   }
9435
9436   if (done)
9437     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9438                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9439                             SND_BD_AMOEBA_TURNING_TO_GEM));
9440 }
9441
9442 void AmoebeWaechst(int x, int y)
9443 {
9444   static unsigned long sound_delay = 0;
9445   static unsigned long sound_delay_value = 0;
9446
9447   if (!MovDelay[x][y])          /* start new growing cycle */
9448   {
9449     MovDelay[x][y] = 7;
9450
9451     if (DelayReached(&sound_delay, sound_delay_value))
9452     {
9453       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9454       sound_delay_value = 30;
9455     }
9456   }
9457
9458   if (MovDelay[x][y])           /* wait some time before growing bigger */
9459   {
9460     MovDelay[x][y]--;
9461     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9462     {
9463       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9464                                            6 - MovDelay[x][y]);
9465
9466       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9467     }
9468
9469     if (!MovDelay[x][y])
9470     {
9471       Feld[x][y] = Store[x][y];
9472       Store[x][y] = 0;
9473       TEST_DrawLevelField(x, y);
9474     }
9475   }
9476 }
9477
9478 void AmoebaDisappearing(int x, int y)
9479 {
9480   static unsigned long sound_delay = 0;
9481   static unsigned long sound_delay_value = 0;
9482
9483   if (!MovDelay[x][y])          /* start new shrinking cycle */
9484   {
9485     MovDelay[x][y] = 7;
9486
9487     if (DelayReached(&sound_delay, sound_delay_value))
9488       sound_delay_value = 30;
9489   }
9490
9491   if (MovDelay[x][y])           /* wait some time before shrinking */
9492   {
9493     MovDelay[x][y]--;
9494     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9495     {
9496       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9497                                            6 - MovDelay[x][y]);
9498
9499       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9500     }
9501
9502     if (!MovDelay[x][y])
9503     {
9504       Feld[x][y] = EL_EMPTY;
9505       TEST_DrawLevelField(x, y);
9506
9507       /* don't let mole enter this field in this cycle;
9508          (give priority to objects falling to this field from above) */
9509       Stop[x][y] = TRUE;
9510     }
9511   }
9512 }
9513
9514 void AmoebeAbleger(int ax, int ay)
9515 {
9516   int i;
9517   int element = Feld[ax][ay];
9518   int graphic = el2img(element);
9519   int newax = ax, neway = ay;
9520   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9521   static int xy[4][2] =
9522   {
9523     { 0, -1 },
9524     { -1, 0 },
9525     { +1, 0 },
9526     { 0, +1 }
9527   };
9528
9529   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9530   {
9531     Feld[ax][ay] = EL_AMOEBA_DEAD;
9532     TEST_DrawLevelField(ax, ay);
9533     return;
9534   }
9535
9536   if (IS_ANIMATED(graphic))
9537     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9538
9539   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9540     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9541
9542   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9543   {
9544     MovDelay[ax][ay]--;
9545     if (MovDelay[ax][ay])
9546       return;
9547   }
9548
9549   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9550   {
9551     int start = RND(4);
9552     int x = ax + xy[start][0];
9553     int y = ay + xy[start][1];
9554
9555     if (!IN_LEV_FIELD(x, y))
9556       return;
9557
9558     if (IS_FREE(x, y) ||
9559         CAN_GROW_INTO(Feld[x][y]) ||
9560         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9561         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9562     {
9563       newax = x;
9564       neway = y;
9565     }
9566
9567     if (newax == ax && neway == ay)
9568       return;
9569   }
9570   else                          /* normal or "filled" (BD style) amoeba */
9571   {
9572     int start = RND(4);
9573     boolean waiting_for_player = FALSE;
9574
9575     for (i = 0; i < NUM_DIRECTIONS; i++)
9576     {
9577       int j = (start + i) % 4;
9578       int x = ax + xy[j][0];
9579       int y = ay + xy[j][1];
9580
9581       if (!IN_LEV_FIELD(x, y))
9582         continue;
9583
9584       if (IS_FREE(x, y) ||
9585           CAN_GROW_INTO(Feld[x][y]) ||
9586           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9587           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9588       {
9589         newax = x;
9590         neway = y;
9591         break;
9592       }
9593       else if (IS_PLAYER(x, y))
9594         waiting_for_player = TRUE;
9595     }
9596
9597     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9598     {
9599       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9600       {
9601         Feld[ax][ay] = EL_AMOEBA_DEAD;
9602         TEST_DrawLevelField(ax, ay);
9603         AmoebaCnt[AmoebaNr[ax][ay]]--;
9604
9605         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9606         {
9607           if (element == EL_AMOEBA_FULL)
9608             AmoebeUmwandeln(ax, ay);
9609           else if (element == EL_BD_AMOEBA)
9610             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9611         }
9612       }
9613       return;
9614     }
9615     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9616     {
9617       /* amoeba gets larger by growing in some direction */
9618
9619       int new_group_nr = AmoebaNr[ax][ay];
9620
9621 #ifdef DEBUG
9622   if (new_group_nr == 0)
9623   {
9624     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9625     printf("AmoebeAbleger(): This should never happen!\n");
9626     return;
9627   }
9628 #endif
9629
9630       AmoebaNr[newax][neway] = new_group_nr;
9631       AmoebaCnt[new_group_nr]++;
9632       AmoebaCnt2[new_group_nr]++;
9633
9634       /* if amoeba touches other amoeba(s) after growing, unify them */
9635       AmoebenVereinigen(newax, neway);
9636
9637       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9638       {
9639         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9640         return;
9641       }
9642     }
9643   }
9644
9645   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9646       (neway == lev_fieldy - 1 && newax != ax))
9647   {
9648     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9649     Store[newax][neway] = element;
9650   }
9651   else if (neway == ay || element == EL_EMC_DRIPPER)
9652   {
9653     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9654
9655     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9656   }
9657   else
9658   {
9659     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9660     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9661     Store[ax][ay] = EL_AMOEBA_DROP;
9662     ContinueMoving(ax, ay);
9663     return;
9664   }
9665
9666   TEST_DrawLevelField(newax, neway);
9667 }
9668
9669 void Life(int ax, int ay)
9670 {
9671   int x1, y1, x2, y2;
9672   int life_time = 40;
9673   int element = Feld[ax][ay];
9674   int graphic = el2img(element);
9675   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9676                          level.biomaze);
9677   boolean changed = FALSE;
9678
9679   if (IS_ANIMATED(graphic))
9680     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9681
9682   if (Stop[ax][ay])
9683     return;
9684
9685   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9686     MovDelay[ax][ay] = life_time;
9687
9688   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9689   {
9690     MovDelay[ax][ay]--;
9691     if (MovDelay[ax][ay])
9692       return;
9693   }
9694
9695   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9696   {
9697     int xx = ax+x1, yy = ay+y1;
9698     int nachbarn = 0;
9699
9700     if (!IN_LEV_FIELD(xx, yy))
9701       continue;
9702
9703     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9704     {
9705       int x = xx+x2, y = yy+y2;
9706
9707       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9708         continue;
9709
9710       if (((Feld[x][y] == element ||
9711             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9712            !Stop[x][y]) ||
9713           (IS_FREE(x, y) && Stop[x][y]))
9714         nachbarn++;
9715     }
9716
9717     if (xx == ax && yy == ay)           /* field in the middle */
9718     {
9719       if (nachbarn < life_parameter[0] ||
9720           nachbarn > life_parameter[1])
9721       {
9722         Feld[xx][yy] = EL_EMPTY;
9723         if (!Stop[xx][yy])
9724           TEST_DrawLevelField(xx, yy);
9725         Stop[xx][yy] = TRUE;
9726         changed = TRUE;
9727       }
9728     }
9729     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9730     {                                   /* free border field */
9731       if (nachbarn >= life_parameter[2] &&
9732           nachbarn <= life_parameter[3])
9733       {
9734         Feld[xx][yy] = element;
9735         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9736         if (!Stop[xx][yy])
9737           TEST_DrawLevelField(xx, yy);
9738         Stop[xx][yy] = TRUE;
9739         changed = TRUE;
9740       }
9741     }
9742   }
9743
9744   if (changed)
9745     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9746                    SND_GAME_OF_LIFE_GROWING);
9747 }
9748
9749 static void InitRobotWheel(int x, int y)
9750 {
9751   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9752 }
9753
9754 static void RunRobotWheel(int x, int y)
9755 {
9756   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9757 }
9758
9759 static void StopRobotWheel(int x, int y)
9760 {
9761   if (ZX == x && ZY == y)
9762   {
9763     ZX = ZY = -1;
9764
9765     game.robot_wheel_active = FALSE;
9766   }
9767 }
9768
9769 static void InitTimegateWheel(int x, int y)
9770 {
9771   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9772 }
9773
9774 static void RunTimegateWheel(int x, int y)
9775 {
9776   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9777 }
9778
9779 static void InitMagicBallDelay(int x, int y)
9780 {
9781 #if 1
9782   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9783 #else
9784   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9785 #endif
9786 }
9787
9788 static void ActivateMagicBall(int bx, int by)
9789 {
9790   int x, y;
9791
9792   if (level.ball_random)
9793   {
9794     int pos_border = RND(8);    /* select one of the eight border elements */
9795     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9796     int xx = pos_content % 3;
9797     int yy = pos_content / 3;
9798
9799     x = bx - 1 + xx;
9800     y = by - 1 + yy;
9801
9802     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9803       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9804   }
9805   else
9806   {
9807     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9808     {
9809       int xx = x - bx + 1;
9810       int yy = y - by + 1;
9811
9812       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9813         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9814     }
9815   }
9816
9817   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9818 }
9819
9820 void CheckExit(int x, int y)
9821 {
9822   if (local_player->gems_still_needed > 0 ||
9823       local_player->sokobanfields_still_needed > 0 ||
9824       local_player->lights_still_needed > 0)
9825   {
9826     int element = Feld[x][y];
9827     int graphic = el2img(element);
9828
9829     if (IS_ANIMATED(graphic))
9830       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9831
9832     return;
9833   }
9834
9835   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9836     return;
9837
9838   Feld[x][y] = EL_EXIT_OPENING;
9839
9840   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9841 }
9842
9843 void CheckExitEM(int x, int y)
9844 {
9845   if (local_player->gems_still_needed > 0 ||
9846       local_player->sokobanfields_still_needed > 0 ||
9847       local_player->lights_still_needed > 0)
9848   {
9849     int element = Feld[x][y];
9850     int graphic = el2img(element);
9851
9852     if (IS_ANIMATED(graphic))
9853       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9854
9855     return;
9856   }
9857
9858   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9859     return;
9860
9861   Feld[x][y] = EL_EM_EXIT_OPENING;
9862
9863   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9864 }
9865
9866 void CheckExitSteel(int x, int y)
9867 {
9868   if (local_player->gems_still_needed > 0 ||
9869       local_player->sokobanfields_still_needed > 0 ||
9870       local_player->lights_still_needed > 0)
9871   {
9872     int element = Feld[x][y];
9873     int graphic = el2img(element);
9874
9875     if (IS_ANIMATED(graphic))
9876       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9877
9878     return;
9879   }
9880
9881   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9882     return;
9883
9884   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9885
9886   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9887 }
9888
9889 void CheckExitSteelEM(int x, int y)
9890 {
9891   if (local_player->gems_still_needed > 0 ||
9892       local_player->sokobanfields_still_needed > 0 ||
9893       local_player->lights_still_needed > 0)
9894   {
9895     int element = Feld[x][y];
9896     int graphic = el2img(element);
9897
9898     if (IS_ANIMATED(graphic))
9899       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9900
9901     return;
9902   }
9903
9904   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9905     return;
9906
9907   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9908
9909   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9910 }
9911
9912 void CheckExitSP(int x, int y)
9913 {
9914   if (local_player->gems_still_needed > 0)
9915   {
9916     int element = Feld[x][y];
9917     int graphic = el2img(element);
9918
9919     if (IS_ANIMATED(graphic))
9920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9921
9922     return;
9923   }
9924
9925   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9926     return;
9927
9928   Feld[x][y] = EL_SP_EXIT_OPENING;
9929
9930   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9931 }
9932
9933 static void CloseAllOpenTimegates()
9934 {
9935   int x, y;
9936
9937   SCAN_PLAYFIELD(x, y)
9938   {
9939     int element = Feld[x][y];
9940
9941     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9942     {
9943       Feld[x][y] = EL_TIMEGATE_CLOSING;
9944
9945       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9946     }
9947   }
9948 }
9949
9950 void DrawTwinkleOnField(int x, int y)
9951 {
9952   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9953     return;
9954
9955   if (Feld[x][y] == EL_BD_DIAMOND)
9956     return;
9957
9958   if (MovDelay[x][y] == 0)      /* next animation frame */
9959     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9960
9961   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9962   {
9963     MovDelay[x][y]--;
9964
9965     DrawLevelElementAnimation(x, y, Feld[x][y]);
9966
9967     if (MovDelay[x][y] != 0)
9968     {
9969       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9970                                            10 - MovDelay[x][y]);
9971
9972       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9973     }
9974   }
9975 }
9976
9977 void MauerWaechst(int x, int y)
9978 {
9979   int delay = 6;
9980
9981   if (!MovDelay[x][y])          /* next animation frame */
9982     MovDelay[x][y] = 3 * delay;
9983
9984   if (MovDelay[x][y])           /* wait some time before next frame */
9985   {
9986     MovDelay[x][y]--;
9987
9988     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9989     {
9990       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9991       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9992
9993       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9994     }
9995
9996     if (!MovDelay[x][y])
9997     {
9998       if (MovDir[x][y] == MV_LEFT)
9999       {
10000         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10001           TEST_DrawLevelField(x - 1, y);
10002       }
10003       else if (MovDir[x][y] == MV_RIGHT)
10004       {
10005         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10006           TEST_DrawLevelField(x + 1, y);
10007       }
10008       else if (MovDir[x][y] == MV_UP)
10009       {
10010         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10011           TEST_DrawLevelField(x, y - 1);
10012       }
10013       else
10014       {
10015         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10016           TEST_DrawLevelField(x, y + 1);
10017       }
10018
10019       Feld[x][y] = Store[x][y];
10020       Store[x][y] = 0;
10021       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10022       TEST_DrawLevelField(x, y);
10023     }
10024   }
10025 }
10026
10027 void MauerAbleger(int ax, int ay)
10028 {
10029   int element = Feld[ax][ay];
10030   int graphic = el2img(element);
10031   boolean oben_frei = FALSE, unten_frei = FALSE;
10032   boolean links_frei = FALSE, rechts_frei = FALSE;
10033   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10034   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10035   boolean new_wall = FALSE;
10036
10037   if (IS_ANIMATED(graphic))
10038     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10039
10040   if (!MovDelay[ax][ay])        /* start building new wall */
10041     MovDelay[ax][ay] = 6;
10042
10043   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10044   {
10045     MovDelay[ax][ay]--;
10046     if (MovDelay[ax][ay])
10047       return;
10048   }
10049
10050   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10051     oben_frei = TRUE;
10052   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10053     unten_frei = TRUE;
10054   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10055     links_frei = TRUE;
10056   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10057     rechts_frei = TRUE;
10058
10059   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10060       element == EL_EXPANDABLE_WALL_ANY)
10061   {
10062     if (oben_frei)
10063     {
10064       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10065       Store[ax][ay-1] = element;
10066       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10067       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10068         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10069                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10070       new_wall = TRUE;
10071     }
10072     if (unten_frei)
10073     {
10074       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10075       Store[ax][ay+1] = element;
10076       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10077       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10078         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10079                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10080       new_wall = TRUE;
10081     }
10082   }
10083
10084   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10085       element == EL_EXPANDABLE_WALL_ANY ||
10086       element == EL_EXPANDABLE_WALL ||
10087       element == EL_BD_EXPANDABLE_WALL)
10088   {
10089     if (links_frei)
10090     {
10091       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10092       Store[ax-1][ay] = element;
10093       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10094       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10095         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10096                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10097       new_wall = TRUE;
10098     }
10099
10100     if (rechts_frei)
10101     {
10102       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10103       Store[ax+1][ay] = element;
10104       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10105       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10106         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10107                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10108       new_wall = TRUE;
10109     }
10110   }
10111
10112   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10113     TEST_DrawLevelField(ax, ay);
10114
10115   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10116     oben_massiv = TRUE;
10117   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10118     unten_massiv = TRUE;
10119   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10120     links_massiv = TRUE;
10121   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10122     rechts_massiv = TRUE;
10123
10124   if (((oben_massiv && unten_massiv) ||
10125        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126        element == EL_EXPANDABLE_WALL) &&
10127       ((links_massiv && rechts_massiv) ||
10128        element == EL_EXPANDABLE_WALL_VERTICAL))
10129     Feld[ax][ay] = EL_WALL;
10130
10131   if (new_wall)
10132     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10133 }
10134
10135 void MauerAblegerStahl(int ax, int ay)
10136 {
10137   int element = Feld[ax][ay];
10138   int graphic = el2img(element);
10139   boolean oben_frei = FALSE, unten_frei = FALSE;
10140   boolean links_frei = FALSE, rechts_frei = FALSE;
10141   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10142   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10143   boolean new_wall = FALSE;
10144
10145   if (IS_ANIMATED(graphic))
10146     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10147
10148   if (!MovDelay[ax][ay])        /* start building new wall */
10149     MovDelay[ax][ay] = 6;
10150
10151   if (MovDelay[ax][ay])         /* wait some time before building new wall */
10152   {
10153     MovDelay[ax][ay]--;
10154     if (MovDelay[ax][ay])
10155       return;
10156   }
10157
10158   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10159     oben_frei = TRUE;
10160   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10161     unten_frei = TRUE;
10162   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10163     links_frei = TRUE;
10164   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10165     rechts_frei = TRUE;
10166
10167   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10168       element == EL_EXPANDABLE_STEELWALL_ANY)
10169   {
10170     if (oben_frei)
10171     {
10172       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10173       Store[ax][ay-1] = element;
10174       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10175       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10176         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10177                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10178       new_wall = TRUE;
10179     }
10180     if (unten_frei)
10181     {
10182       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10183       Store[ax][ay+1] = element;
10184       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10185       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10186         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10187                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10188       new_wall = TRUE;
10189     }
10190   }
10191
10192   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10193       element == EL_EXPANDABLE_STEELWALL_ANY)
10194   {
10195     if (links_frei)
10196     {
10197       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10198       Store[ax-1][ay] = element;
10199       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10200       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10201         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10202                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10203       new_wall = TRUE;
10204     }
10205
10206     if (rechts_frei)
10207     {
10208       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10209       Store[ax+1][ay] = element;
10210       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10211       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10212         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10213                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10214       new_wall = TRUE;
10215     }
10216   }
10217
10218   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10219     oben_massiv = TRUE;
10220   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10221     unten_massiv = TRUE;
10222   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10223     links_massiv = TRUE;
10224   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10225     rechts_massiv = TRUE;
10226
10227   if (((oben_massiv && unten_massiv) ||
10228        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10229       ((links_massiv && rechts_massiv) ||
10230        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10231     Feld[ax][ay] = EL_STEELWALL;
10232
10233   if (new_wall)
10234     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10235 }
10236
10237 void CheckForDragon(int x, int y)
10238 {
10239   int i, j;
10240   boolean dragon_found = FALSE;
10241   static int xy[4][2] =
10242   {
10243     { 0, -1 },
10244     { -1, 0 },
10245     { +1, 0 },
10246     { 0, +1 }
10247   };
10248
10249   for (i = 0; i < NUM_DIRECTIONS; i++)
10250   {
10251     for (j = 0; j < 4; j++)
10252     {
10253       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10254
10255       if (IN_LEV_FIELD(xx, yy) &&
10256           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10257       {
10258         if (Feld[xx][yy] == EL_DRAGON)
10259           dragon_found = TRUE;
10260       }
10261       else
10262         break;
10263     }
10264   }
10265
10266   if (!dragon_found)
10267   {
10268     for (i = 0; i < NUM_DIRECTIONS; i++)
10269     {
10270       for (j = 0; j < 3; j++)
10271       {
10272         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10273   
10274         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10275         {
10276           Feld[xx][yy] = EL_EMPTY;
10277           TEST_DrawLevelField(xx, yy);
10278         }
10279         else
10280           break;
10281       }
10282     }
10283   }
10284 }
10285
10286 static void InitBuggyBase(int x, int y)
10287 {
10288   int element = Feld[x][y];
10289   int activating_delay = FRAMES_PER_SECOND / 4;
10290
10291   ChangeDelay[x][y] =
10292     (element == EL_SP_BUGGY_BASE ?
10293      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10294      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10295      activating_delay :
10296      element == EL_SP_BUGGY_BASE_ACTIVE ?
10297      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10298 }
10299
10300 static void WarnBuggyBase(int x, int y)
10301 {
10302   int i;
10303   static int xy[4][2] =
10304   {
10305     { 0, -1 },
10306     { -1, 0 },
10307     { +1, 0 },
10308     { 0, +1 }
10309   };
10310
10311   for (i = 0; i < NUM_DIRECTIONS; i++)
10312   {
10313     int xx = x + xy[i][0];
10314     int yy = y + xy[i][1];
10315
10316     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10317     {
10318       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10319
10320       break;
10321     }
10322   }
10323 }
10324
10325 static void InitTrap(int x, int y)
10326 {
10327   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10328 }
10329
10330 static void ActivateTrap(int x, int y)
10331 {
10332   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10333 }
10334
10335 static void ChangeActiveTrap(int x, int y)
10336 {
10337   int graphic = IMG_TRAP_ACTIVE;
10338
10339   /* if new animation frame was drawn, correct crumbled sand border */
10340   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10341     TEST_DrawLevelFieldCrumbledSand(x, y);
10342 }
10343
10344 static int getSpecialActionElement(int element, int number, int base_element)
10345 {
10346   return (element != EL_EMPTY ? element :
10347           number != -1 ? base_element + number - 1 :
10348           EL_EMPTY);
10349 }
10350
10351 static int getModifiedActionNumber(int value_old, int operator, int operand,
10352                                    int value_min, int value_max)
10353 {
10354   int value_new = (operator == CA_MODE_SET      ? operand :
10355                    operator == CA_MODE_ADD      ? value_old + operand :
10356                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10357                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10358                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10359                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10360                    value_old);
10361
10362   return (value_new < value_min ? value_min :
10363           value_new > value_max ? value_max :
10364           value_new);
10365 }
10366
10367 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10368 {
10369   struct ElementInfo *ei = &element_info[element];
10370   struct ElementChangeInfo *change = &ei->change_page[page];
10371   int target_element = change->target_element;
10372   int action_type = change->action_type;
10373   int action_mode = change->action_mode;
10374   int action_arg = change->action_arg;
10375   int action_element = change->action_element;
10376   int i;
10377
10378   if (!change->has_action)
10379     return;
10380
10381   /* ---------- determine action paramater values -------------------------- */
10382
10383   int level_time_value =
10384     (level.time > 0 ? TimeLeft :
10385      TimePlayed);
10386
10387   int action_arg_element_raw =
10388     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10389      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10390      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10391      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10392      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10393      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10394      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10395      EL_EMPTY);
10396   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10397
10398 #if 0
10399   if (action_arg_element_raw == EL_GROUP_START)
10400     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10401 #endif
10402
10403   int action_arg_direction =
10404     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10405      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10406      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10407      change->actual_trigger_side :
10408      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10409      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10410      MV_NONE);
10411
10412   int action_arg_number_min =
10413     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10414      CA_ARG_MIN);
10415
10416   int action_arg_number_max =
10417     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10418      action_type == CA_SET_LEVEL_GEMS ? 999 :
10419      action_type == CA_SET_LEVEL_TIME ? 9999 :
10420      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10421      action_type == CA_SET_CE_VALUE ? 9999 :
10422      action_type == CA_SET_CE_SCORE ? 9999 :
10423      CA_ARG_MAX);
10424
10425   int action_arg_number_reset =
10426     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10427      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10428      action_type == CA_SET_LEVEL_TIME ? level.time :
10429      action_type == CA_SET_LEVEL_SCORE ? 0 :
10430 #if USE_NEW_CUSTOM_VALUE
10431      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10432 #else
10433      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10434 #endif
10435      action_type == CA_SET_CE_SCORE ? 0 :
10436      0);
10437
10438   int action_arg_number =
10439     (action_arg <= CA_ARG_MAX ? action_arg :
10440      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10441      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10442      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10443      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10444      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10445      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10446 #if USE_NEW_CUSTOM_VALUE
10447      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10448 #else
10449      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10450 #endif
10451      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10452      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10453      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10454      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10455      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10456      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10457      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10458      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10459      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10460      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10461      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10462      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10463      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10464      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10465      -1);
10466
10467   int action_arg_number_old =
10468     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10469      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10470      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10471      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10472      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10473      0);
10474
10475   int action_arg_number_new =
10476     getModifiedActionNumber(action_arg_number_old,
10477                             action_mode, action_arg_number,
10478                             action_arg_number_min, action_arg_number_max);
10479
10480 #if 1
10481   int trigger_player_bits =
10482     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10483      change->actual_trigger_player_bits : change->trigger_player);
10484 #else
10485   int trigger_player_bits =
10486     (change->actual_trigger_player >= EL_PLAYER_1 &&
10487      change->actual_trigger_player <= EL_PLAYER_4 ?
10488      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10489      PLAYER_BITS_ANY);
10490 #endif
10491
10492   int action_arg_player_bits =
10493     (action_arg >= CA_ARG_PLAYER_1 &&
10494      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10495      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10496      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10497      PLAYER_BITS_ANY);
10498
10499   /* ---------- execute action  -------------------------------------------- */
10500
10501   switch (action_type)
10502   {
10503     case CA_NO_ACTION:
10504     {
10505       return;
10506     }
10507
10508     /* ---------- level actions  ------------------------------------------- */
10509
10510     case CA_RESTART_LEVEL:
10511     {
10512       game.restart_level = TRUE;
10513
10514       break;
10515     }
10516
10517     case CA_SHOW_ENVELOPE:
10518     {
10519       int element = getSpecialActionElement(action_arg_element,
10520                                             action_arg_number, EL_ENVELOPE_1);
10521
10522       if (IS_ENVELOPE(element))
10523         local_player->show_envelope = element;
10524
10525       break;
10526     }
10527
10528     case CA_SET_LEVEL_TIME:
10529     {
10530       if (level.time > 0)       /* only modify limited time value */
10531       {
10532         TimeLeft = action_arg_number_new;
10533
10534 #if 1
10535         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10536
10537         DisplayGameControlValues();
10538 #else
10539         DrawGameValue_Time(TimeLeft);
10540 #endif
10541
10542         if (!TimeLeft && setup.time_limit)
10543           for (i = 0; i < MAX_PLAYERS; i++)
10544             KillPlayer(&stored_player[i]);
10545       }
10546
10547       break;
10548     }
10549
10550     case CA_SET_LEVEL_SCORE:
10551     {
10552       local_player->score = action_arg_number_new;
10553
10554 #if 1
10555       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10556
10557       DisplayGameControlValues();
10558 #else
10559       DrawGameValue_Score(local_player->score);
10560 #endif
10561
10562       break;
10563     }
10564
10565     case CA_SET_LEVEL_GEMS:
10566     {
10567       local_player->gems_still_needed = action_arg_number_new;
10568
10569 #if 1
10570       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10571
10572       DisplayGameControlValues();
10573 #else
10574       DrawGameValue_Emeralds(local_player->gems_still_needed);
10575 #endif
10576
10577       break;
10578     }
10579
10580 #if !USE_PLAYER_GRAVITY
10581     case CA_SET_LEVEL_GRAVITY:
10582     {
10583       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10584                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10585                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10586                       game.gravity);
10587       break;
10588     }
10589 #endif
10590
10591     case CA_SET_LEVEL_WIND:
10592     {
10593       game.wind_direction = action_arg_direction;
10594
10595       break;
10596     }
10597
10598     case CA_SET_LEVEL_RANDOM_SEED:
10599     {
10600 #if 1
10601       /* ensure that setting a new random seed while playing is predictable */
10602       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10603 #else
10604       InitRND(action_arg_number_new);
10605 #endif
10606
10607 #if 0
10608       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10609 #endif
10610
10611 #if 0
10612       {
10613         int i;
10614
10615         printf("::: ");
10616         for (i = 0; i < 9; i++)
10617           printf("%d, ", RND(2));
10618         printf("\n");
10619       }
10620 #endif
10621
10622       break;
10623     }
10624
10625     /* ---------- player actions  ------------------------------------------ */
10626
10627     case CA_MOVE_PLAYER:
10628     {
10629       /* automatically move to the next field in specified direction */
10630       for (i = 0; i < MAX_PLAYERS; i++)
10631         if (trigger_player_bits & (1 << i))
10632           stored_player[i].programmed_action = action_arg_direction;
10633
10634       break;
10635     }
10636
10637     case CA_EXIT_PLAYER:
10638     {
10639       for (i = 0; i < MAX_PLAYERS; i++)
10640         if (action_arg_player_bits & (1 << i))
10641           PlayerWins(&stored_player[i]);
10642
10643       break;
10644     }
10645
10646     case CA_KILL_PLAYER:
10647     {
10648       for (i = 0; i < MAX_PLAYERS; i++)
10649         if (action_arg_player_bits & (1 << i))
10650           KillPlayer(&stored_player[i]);
10651
10652       break;
10653     }
10654
10655     case CA_SET_PLAYER_KEYS:
10656     {
10657       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10658       int element = getSpecialActionElement(action_arg_element,
10659                                             action_arg_number, EL_KEY_1);
10660
10661       if (IS_KEY(element))
10662       {
10663         for (i = 0; i < MAX_PLAYERS; i++)
10664         {
10665           if (trigger_player_bits & (1 << i))
10666           {
10667             stored_player[i].key[KEY_NR(element)] = key_state;
10668
10669             DrawGameDoorValues();
10670           }
10671         }
10672       }
10673
10674       break;
10675     }
10676
10677     case CA_SET_PLAYER_SPEED:
10678     {
10679 #if 0
10680       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10681 #endif
10682
10683       for (i = 0; i < MAX_PLAYERS; i++)
10684       {
10685         if (trigger_player_bits & (1 << i))
10686         {
10687           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10688
10689           if (action_arg == CA_ARG_SPEED_FASTER &&
10690               stored_player[i].cannot_move)
10691           {
10692             action_arg_number = STEPSIZE_VERY_SLOW;
10693           }
10694           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10695                    action_arg == CA_ARG_SPEED_FASTER)
10696           {
10697             action_arg_number = 2;
10698             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10699                            CA_MODE_MULTIPLY);
10700           }
10701           else if (action_arg == CA_ARG_NUMBER_RESET)
10702           {
10703             action_arg_number = level.initial_player_stepsize[i];
10704           }
10705
10706           move_stepsize =
10707             getModifiedActionNumber(move_stepsize,
10708                                     action_mode,
10709                                     action_arg_number,
10710                                     action_arg_number_min,
10711                                     action_arg_number_max);
10712
10713           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10714         }
10715       }
10716
10717       break;
10718     }
10719
10720     case CA_SET_PLAYER_SHIELD:
10721     {
10722       for (i = 0; i < MAX_PLAYERS; i++)
10723       {
10724         if (trigger_player_bits & (1 << i))
10725         {
10726           if (action_arg == CA_ARG_SHIELD_OFF)
10727           {
10728             stored_player[i].shield_normal_time_left = 0;
10729             stored_player[i].shield_deadly_time_left = 0;
10730           }
10731           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10732           {
10733             stored_player[i].shield_normal_time_left = 999999;
10734           }
10735           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10736           {
10737             stored_player[i].shield_normal_time_left = 999999;
10738             stored_player[i].shield_deadly_time_left = 999999;
10739           }
10740         }
10741       }
10742
10743       break;
10744     }
10745
10746 #if USE_PLAYER_GRAVITY
10747     case CA_SET_PLAYER_GRAVITY:
10748     {
10749       for (i = 0; i < MAX_PLAYERS; i++)
10750       {
10751         if (trigger_player_bits & (1 << i))
10752         {
10753           stored_player[i].gravity =
10754             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10755              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10756              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10757              stored_player[i].gravity);
10758         }
10759       }
10760
10761       break;
10762     }
10763 #endif
10764
10765     case CA_SET_PLAYER_ARTWORK:
10766     {
10767       for (i = 0; i < MAX_PLAYERS; i++)
10768       {
10769         if (trigger_player_bits & (1 << i))
10770         {
10771           int artwork_element = action_arg_element;
10772
10773           if (action_arg == CA_ARG_ELEMENT_RESET)
10774             artwork_element =
10775               (level.use_artwork_element[i] ? level.artwork_element[i] :
10776                stored_player[i].element_nr);
10777
10778 #if USE_GFX_RESET_PLAYER_ARTWORK
10779           if (stored_player[i].artwork_element != artwork_element)
10780             stored_player[i].Frame = 0;
10781 #endif
10782
10783           stored_player[i].artwork_element = artwork_element;
10784
10785           SetPlayerWaiting(&stored_player[i], FALSE);
10786
10787           /* set number of special actions for bored and sleeping animation */
10788           stored_player[i].num_special_action_bored =
10789             get_num_special_action(artwork_element,
10790                                    ACTION_BORING_1, ACTION_BORING_LAST);
10791           stored_player[i].num_special_action_sleeping =
10792             get_num_special_action(artwork_element,
10793                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10794         }
10795       }
10796
10797       break;
10798     }
10799
10800     case CA_SET_PLAYER_INVENTORY:
10801     {
10802       for (i = 0; i < MAX_PLAYERS; i++)
10803       {
10804         struct PlayerInfo *player = &stored_player[i];
10805         int j, k;
10806
10807         if (trigger_player_bits & (1 << i))
10808         {
10809           int inventory_element = action_arg_element;
10810
10811           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10812               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10813               action_arg == CA_ARG_ELEMENT_ACTION)
10814           {
10815             int element = inventory_element;
10816             int collect_count = element_info[element].collect_count_initial;
10817
10818             if (!IS_CUSTOM_ELEMENT(element))
10819               collect_count = 1;
10820
10821             if (collect_count == 0)
10822               player->inventory_infinite_element = element;
10823             else
10824               for (k = 0; k < collect_count; k++)
10825                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10826                   player->inventory_element[player->inventory_size++] =
10827                     element;
10828           }
10829           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10830                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10831                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10832           {
10833             if (player->inventory_infinite_element != EL_UNDEFINED &&
10834                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10835                                      action_arg_element_raw))
10836               player->inventory_infinite_element = EL_UNDEFINED;
10837
10838             for (k = 0, j = 0; j < player->inventory_size; j++)
10839             {
10840               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10841                                         action_arg_element_raw))
10842                 player->inventory_element[k++] = player->inventory_element[j];
10843             }
10844
10845             player->inventory_size = k;
10846           }
10847           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10848           {
10849             if (player->inventory_size > 0)
10850             {
10851               for (j = 0; j < player->inventory_size - 1; j++)
10852                 player->inventory_element[j] = player->inventory_element[j + 1];
10853
10854               player->inventory_size--;
10855             }
10856           }
10857           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10858           {
10859             if (player->inventory_size > 0)
10860               player->inventory_size--;
10861           }
10862           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10863           {
10864             player->inventory_infinite_element = EL_UNDEFINED;
10865             player->inventory_size = 0;
10866           }
10867           else if (action_arg == CA_ARG_INVENTORY_RESET)
10868           {
10869             player->inventory_infinite_element = EL_UNDEFINED;
10870             player->inventory_size = 0;
10871
10872             if (level.use_initial_inventory[i])
10873             {
10874               for (j = 0; j < level.initial_inventory_size[i]; j++)
10875               {
10876                 int element = level.initial_inventory_content[i][j];
10877                 int collect_count = element_info[element].collect_count_initial;
10878
10879                 if (!IS_CUSTOM_ELEMENT(element))
10880                   collect_count = 1;
10881
10882                 if (collect_count == 0)
10883                   player->inventory_infinite_element = element;
10884                 else
10885                   for (k = 0; k < collect_count; k++)
10886                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10887                       player->inventory_element[player->inventory_size++] =
10888                         element;
10889               }
10890             }
10891           }
10892         }
10893       }
10894
10895       break;
10896     }
10897
10898     /* ---------- CE actions  ---------------------------------------------- */
10899
10900     case CA_SET_CE_VALUE:
10901     {
10902 #if USE_NEW_CUSTOM_VALUE
10903       int last_ce_value = CustomValue[x][y];
10904
10905       CustomValue[x][y] = action_arg_number_new;
10906
10907       if (CustomValue[x][y] != last_ce_value)
10908       {
10909         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10910         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10911
10912         if (CustomValue[x][y] == 0)
10913         {
10914           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10915           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10916         }
10917       }
10918 #endif
10919
10920       break;
10921     }
10922
10923     case CA_SET_CE_SCORE:
10924     {
10925 #if USE_NEW_CUSTOM_VALUE
10926       int last_ce_score = ei->collect_score;
10927
10928       ei->collect_score = action_arg_number_new;
10929
10930       if (ei->collect_score != last_ce_score)
10931       {
10932         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10933         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10934
10935         if (ei->collect_score == 0)
10936         {
10937           int xx, yy;
10938
10939           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10940           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10941
10942           /*
10943             This is a very special case that seems to be a mixture between
10944             CheckElementChange() and CheckTriggeredElementChange(): while
10945             the first one only affects single elements that are triggered
10946             directly, the second one affects multiple elements in the playfield
10947             that are triggered indirectly by another element. This is a third
10948             case: Changing the CE score always affects multiple identical CEs,
10949             so every affected CE must be checked, not only the single CE for
10950             which the CE score was changed in the first place (as every instance
10951             of that CE shares the same CE score, and therefore also can change)!
10952           */
10953           SCAN_PLAYFIELD(xx, yy)
10954           {
10955             if (Feld[xx][yy] == element)
10956               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10957                                  CE_SCORE_GETS_ZERO);
10958           }
10959         }
10960       }
10961 #endif
10962
10963       break;
10964     }
10965
10966     case CA_SET_CE_ARTWORK:
10967     {
10968       int artwork_element = action_arg_element;
10969       boolean reset_frame = FALSE;
10970       int xx, yy;
10971
10972       if (action_arg == CA_ARG_ELEMENT_RESET)
10973         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10974                            element);
10975
10976       if (ei->gfx_element != artwork_element)
10977         reset_frame = TRUE;
10978
10979       ei->gfx_element = artwork_element;
10980
10981       SCAN_PLAYFIELD(xx, yy)
10982       {
10983         if (Feld[xx][yy] == element)
10984         {
10985           if (reset_frame)
10986           {
10987             ResetGfxAnimation(xx, yy);
10988             ResetRandomAnimationValue(xx, yy);
10989           }
10990
10991           TEST_DrawLevelField(xx, yy);
10992         }
10993       }
10994
10995       break;
10996     }
10997
10998     /* ---------- engine actions  ------------------------------------------ */
10999
11000     case CA_SET_ENGINE_SCAN_MODE:
11001     {
11002       InitPlayfieldScanMode(action_arg);
11003
11004       break;
11005     }
11006
11007     default:
11008       break;
11009   }
11010 }
11011
11012 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11013 {
11014   int old_element = Feld[x][y];
11015   int new_element = GetElementFromGroupElement(element);
11016   int previous_move_direction = MovDir[x][y];
11017 #if USE_NEW_CUSTOM_VALUE
11018   int last_ce_value = CustomValue[x][y];
11019 #endif
11020   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11021   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11022   boolean add_player_onto_element = (new_element_is_player &&
11023 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11024                                      /* this breaks SnakeBite when a snake is
11025                                         halfway through a door that closes */
11026                                      /* NOW FIXED AT LEVEL INIT IN files.c */
11027                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
11028 #endif
11029                                      IS_WALKABLE(old_element));
11030
11031 #if 0
11032   /* check if element under the player changes from accessible to unaccessible
11033      (needed for special case of dropping element which then changes) */
11034   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11035       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11036   {
11037     Bang(x, y);
11038
11039     return;
11040   }
11041 #endif
11042
11043   if (!add_player_onto_element)
11044   {
11045     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11046       RemoveMovingField(x, y);
11047     else
11048       RemoveField(x, y);
11049
11050     Feld[x][y] = new_element;
11051
11052 #if !USE_GFX_RESET_GFX_ANIMATION
11053     ResetGfxAnimation(x, y);
11054     ResetRandomAnimationValue(x, y);
11055 #endif
11056
11057     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11058       MovDir[x][y] = previous_move_direction;
11059
11060 #if USE_NEW_CUSTOM_VALUE
11061     if (element_info[new_element].use_last_ce_value)
11062       CustomValue[x][y] = last_ce_value;
11063 #endif
11064
11065     InitField_WithBug1(x, y, FALSE);
11066
11067     new_element = Feld[x][y];   /* element may have changed */
11068
11069 #if USE_GFX_RESET_GFX_ANIMATION
11070     ResetGfxAnimation(x, y);
11071     ResetRandomAnimationValue(x, y);
11072 #endif
11073
11074     TEST_DrawLevelField(x, y);
11075
11076     if (GFX_CRUMBLED(new_element))
11077       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11078   }
11079
11080 #if 1
11081   /* check if element under the player changes from accessible to unaccessible
11082      (needed for special case of dropping element which then changes) */
11083   /* (must be checked after creating new element for walkable group elements) */
11084 #if USE_FIX_KILLED_BY_NON_WALKABLE
11085   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11086       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11087   {
11088     Bang(x, y);
11089
11090     return;
11091   }
11092 #else
11093   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11094       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11095   {
11096     Bang(x, y);
11097
11098     return;
11099   }
11100 #endif
11101 #endif
11102
11103   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11104   if (new_element_is_player)
11105     RelocatePlayer(x, y, new_element);
11106
11107   if (is_change)
11108     ChangeCount[x][y]++;        /* count number of changes in the same frame */
11109
11110   TestIfBadThingTouchesPlayer(x, y);
11111   TestIfPlayerTouchesCustomElement(x, y);
11112   TestIfElementTouchesCustomElement(x, y);
11113 }
11114
11115 static void CreateField(int x, int y, int element)
11116 {
11117   CreateFieldExt(x, y, element, FALSE);
11118 }
11119
11120 static void CreateElementFromChange(int x, int y, int element)
11121 {
11122   element = GET_VALID_RUNTIME_ELEMENT(element);
11123
11124 #if USE_STOP_CHANGED_ELEMENTS
11125   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11126   {
11127     int old_element = Feld[x][y];
11128
11129     /* prevent changed element from moving in same engine frame
11130        unless both old and new element can either fall or move */
11131     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11132         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11133       Stop[x][y] = TRUE;
11134   }
11135 #endif
11136
11137   CreateFieldExt(x, y, element, TRUE);
11138 }
11139
11140 static boolean ChangeElement(int x, int y, int element, int page)
11141 {
11142   struct ElementInfo *ei = &element_info[element];
11143   struct ElementChangeInfo *change = &ei->change_page[page];
11144   int ce_value = CustomValue[x][y];
11145   int ce_score = ei->collect_score;
11146   int target_element;
11147   int old_element = Feld[x][y];
11148
11149   /* always use default change event to prevent running into a loop */
11150   if (ChangeEvent[x][y] == -1)
11151     ChangeEvent[x][y] = CE_DELAY;
11152
11153   if (ChangeEvent[x][y] == CE_DELAY)
11154   {
11155     /* reset actual trigger element, trigger player and action element */
11156     change->actual_trigger_element = EL_EMPTY;
11157     change->actual_trigger_player = EL_EMPTY;
11158     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11159     change->actual_trigger_side = CH_SIDE_NONE;
11160     change->actual_trigger_ce_value = 0;
11161     change->actual_trigger_ce_score = 0;
11162   }
11163
11164   /* do not change elements more than a specified maximum number of changes */
11165   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11166     return FALSE;
11167
11168   ChangeCount[x][y]++;          /* count number of changes in the same frame */
11169
11170   if (change->explode)
11171   {
11172     Bang(x, y);
11173
11174     return TRUE;
11175   }
11176
11177   if (change->use_target_content)
11178   {
11179     boolean complete_replace = TRUE;
11180     boolean can_replace[3][3];
11181     int xx, yy;
11182
11183     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11184     {
11185       boolean is_empty;
11186       boolean is_walkable;
11187       boolean is_diggable;
11188       boolean is_collectible;
11189       boolean is_removable;
11190       boolean is_destructible;
11191       int ex = x + xx - 1;
11192       int ey = y + yy - 1;
11193       int content_element = change->target_content.e[xx][yy];
11194       int e;
11195
11196       can_replace[xx][yy] = TRUE;
11197
11198       if (ex == x && ey == y)   /* do not check changing element itself */
11199         continue;
11200
11201       if (content_element == EL_EMPTY_SPACE)
11202       {
11203         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
11204
11205         continue;
11206       }
11207
11208       if (!IN_LEV_FIELD(ex, ey))
11209       {
11210         can_replace[xx][yy] = FALSE;
11211         complete_replace = FALSE;
11212
11213         continue;
11214       }
11215
11216       e = Feld[ex][ey];
11217
11218       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11219         e = MovingOrBlocked2Element(ex, ey);
11220
11221       is_empty = (IS_FREE(ex, ey) ||
11222                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11223
11224       is_walkable     = (is_empty || IS_WALKABLE(e));
11225       is_diggable     = (is_empty || IS_DIGGABLE(e));
11226       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11227       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11228       is_removable    = (is_diggable || is_collectible);
11229
11230       can_replace[xx][yy] =
11231         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11232           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11233           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11234           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11235           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11236           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11237          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11238
11239       if (!can_replace[xx][yy])
11240         complete_replace = FALSE;
11241     }
11242
11243     if (!change->only_if_complete || complete_replace)
11244     {
11245       boolean something_has_changed = FALSE;
11246
11247       if (change->only_if_complete && change->use_random_replace &&
11248           RND(100) < change->random_percentage)
11249         return FALSE;
11250
11251       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11252       {
11253         int ex = x + xx - 1;
11254         int ey = y + yy - 1;
11255         int content_element;
11256
11257         if (can_replace[xx][yy] && (!change->use_random_replace ||
11258                                     RND(100) < change->random_percentage))
11259         {
11260           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11261             RemoveMovingField(ex, ey);
11262
11263           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11264
11265           content_element = change->target_content.e[xx][yy];
11266           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11267                                               ce_value, ce_score);
11268
11269           CreateElementFromChange(ex, ey, target_element);
11270
11271           something_has_changed = TRUE;
11272
11273           /* for symmetry reasons, freeze newly created border elements */
11274           if (ex != x || ey != y)
11275             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
11276         }
11277       }
11278
11279       if (something_has_changed)
11280       {
11281         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11282         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11283       }
11284     }
11285   }
11286   else
11287   {
11288     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11289                                         ce_value, ce_score);
11290
11291     if (element == EL_DIAGONAL_GROWING ||
11292         element == EL_DIAGONAL_SHRINKING)
11293     {
11294       target_element = Store[x][y];
11295
11296       Store[x][y] = EL_EMPTY;
11297     }
11298
11299     CreateElementFromChange(x, y, target_element);
11300
11301     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11302     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11303   }
11304
11305   /* this uses direct change before indirect change */
11306   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11307
11308   return TRUE;
11309 }
11310
11311 #if USE_NEW_DELAYED_ACTION
11312
11313 static void HandleElementChange(int x, int y, int page)
11314 {
11315   int element = MovingOrBlocked2Element(x, y);
11316   struct ElementInfo *ei = &element_info[element];
11317   struct ElementChangeInfo *change = &ei->change_page[page];
11318   boolean handle_action_before_change = FALSE;
11319
11320 #ifdef DEBUG
11321   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11322       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11323   {
11324     printf("\n\n");
11325     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11326            x, y, element, element_info[element].token_name);
11327     printf("HandleElementChange(): This should never happen!\n");
11328     printf("\n\n");
11329   }
11330 #endif
11331
11332   /* this can happen with classic bombs on walkable, changing elements */
11333   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11334   {
11335 #if 0
11336     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11337       ChangeDelay[x][y] = 0;
11338 #endif
11339
11340     return;
11341   }
11342
11343   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11344   {
11345     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11346
11347     if (change->can_change)
11348     {
11349 #if 1
11350       /* !!! not clear why graphic animation should be reset at all here !!! */
11351       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11352 #if USE_GFX_RESET_WHEN_NOT_MOVING
11353       /* when a custom element is about to change (for example by change delay),
11354          do not reset graphic animation when the custom element is moving */
11355       if (!IS_MOVING(x, y))
11356 #endif
11357       {
11358         ResetGfxAnimation(x, y);
11359         ResetRandomAnimationValue(x, y);
11360       }
11361 #endif
11362
11363       if (change->pre_change_function)
11364         change->pre_change_function(x, y);
11365     }
11366   }
11367
11368   ChangeDelay[x][y]--;
11369
11370   if (ChangeDelay[x][y] != 0)           /* continue element change */
11371   {
11372     if (change->can_change)
11373     {
11374       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11375
11376       if (IS_ANIMATED(graphic))
11377         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11378
11379       if (change->change_function)
11380         change->change_function(x, y);
11381     }
11382   }
11383   else                                  /* finish element change */
11384   {
11385     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11386     {
11387       page = ChangePage[x][y];
11388       ChangePage[x][y] = -1;
11389
11390       change = &ei->change_page[page];
11391     }
11392
11393     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11394     {
11395       ChangeDelay[x][y] = 1;            /* try change after next move step */
11396       ChangePage[x][y] = page;          /* remember page to use for change */
11397
11398       return;
11399     }
11400
11401 #if 1
11402     /* special case: set new level random seed before changing element */
11403     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11404       handle_action_before_change = TRUE;
11405
11406     if (change->has_action && handle_action_before_change)
11407       ExecuteCustomElementAction(x, y, element, page);
11408 #endif
11409
11410     if (change->can_change)
11411     {
11412       if (ChangeElement(x, y, element, page))
11413       {
11414         if (change->post_change_function)
11415           change->post_change_function(x, y);
11416       }
11417     }
11418
11419     if (change->has_action && !handle_action_before_change)
11420       ExecuteCustomElementAction(x, y, element, page);
11421   }
11422 }
11423
11424 #else
11425
11426 static void HandleElementChange(int x, int y, int page)
11427 {
11428   int element = MovingOrBlocked2Element(x, y);
11429   struct ElementInfo *ei = &element_info[element];
11430   struct ElementChangeInfo *change = &ei->change_page[page];
11431
11432 #ifdef DEBUG
11433   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11434   {
11435     printf("\n\n");
11436     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11437            x, y, element, element_info[element].token_name);
11438     printf("HandleElementChange(): This should never happen!\n");
11439     printf("\n\n");
11440   }
11441 #endif
11442
11443   /* this can happen with classic bombs on walkable, changing elements */
11444   if (!CAN_CHANGE(element))
11445   {
11446 #if 0
11447     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
11448       ChangeDelay[x][y] = 0;
11449 #endif
11450
11451     return;
11452   }
11453
11454   if (ChangeDelay[x][y] == 0)           /* initialize element change */
11455   {
11456     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11457
11458     ResetGfxAnimation(x, y);
11459     ResetRandomAnimationValue(x, y);
11460
11461     if (change->pre_change_function)
11462       change->pre_change_function(x, y);
11463   }
11464
11465   ChangeDelay[x][y]--;
11466
11467   if (ChangeDelay[x][y] != 0)           /* continue element change */
11468   {
11469     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11470
11471     if (IS_ANIMATED(graphic))
11472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11473
11474     if (change->change_function)
11475       change->change_function(x, y);
11476   }
11477   else                                  /* finish element change */
11478   {
11479     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
11480     {
11481       page = ChangePage[x][y];
11482       ChangePage[x][y] = -1;
11483
11484       change = &ei->change_page[page];
11485     }
11486
11487     if (IS_MOVING(x, y))                /* never change a running system ;-) */
11488     {
11489       ChangeDelay[x][y] = 1;            /* try change after next move step */
11490       ChangePage[x][y] = page;          /* remember page to use for change */
11491
11492       return;
11493     }
11494
11495     if (ChangeElement(x, y, element, page))
11496     {
11497       if (change->post_change_function)
11498         change->post_change_function(x, y);
11499     }
11500   }
11501 }
11502
11503 #endif
11504
11505 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11506                                               int trigger_element,
11507                                               int trigger_event,
11508                                               int trigger_player,
11509                                               int trigger_side,
11510                                               int trigger_page)
11511 {
11512   boolean change_done_any = FALSE;
11513   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11514   int i;
11515
11516   if (!(trigger_events[trigger_element][trigger_event]))
11517     return FALSE;
11518
11519 #if 0
11520   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11521          trigger_event, recursion_loop_depth, recursion_loop_detected,
11522          recursion_loop_element, EL_NAME(recursion_loop_element));
11523 #endif
11524
11525   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11526
11527   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11528   {
11529     int element = EL_CUSTOM_START + i;
11530     boolean change_done = FALSE;
11531     int p;
11532
11533     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11534         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11535       continue;
11536
11537     for (p = 0; p < element_info[element].num_change_pages; p++)
11538     {
11539       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11540
11541       if (change->can_change_or_has_action &&
11542           change->has_event[trigger_event] &&
11543           change->trigger_side & trigger_side &&
11544           change->trigger_player & trigger_player &&
11545           change->trigger_page & trigger_page_bits &&
11546           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11547       {
11548         change->actual_trigger_element = trigger_element;
11549         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11550         change->actual_trigger_player_bits = trigger_player;
11551         change->actual_trigger_side = trigger_side;
11552         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11553         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11554
11555 #if 0
11556         printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11557                element, EL_NAME(element), p);
11558 #endif
11559
11560         if ((change->can_change && !change_done) || change->has_action)
11561         {
11562           int x, y;
11563
11564           SCAN_PLAYFIELD(x, y)
11565           {
11566             if (Feld[x][y] == element)
11567             {
11568               if (change->can_change && !change_done)
11569               {
11570 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11571                 /* if element already changed in this frame, not only prevent
11572                    another element change (checked in ChangeElement()), but
11573                    also prevent additional element actions for this element */
11574
11575                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11576                     !level.use_action_after_change_bug)
11577                   continue;
11578 #endif
11579
11580 #if 0
11581                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11582                        element, EL_NAME(element), p);
11583 #endif
11584
11585                 ChangeDelay[x][y] = 1;
11586                 ChangeEvent[x][y] = trigger_event;
11587
11588                 HandleElementChange(x, y, p);
11589               }
11590 #if USE_NEW_DELAYED_ACTION
11591               else if (change->has_action)
11592               {
11593 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11594                 /* if element already changed in this frame, not only prevent
11595                    another element change (checked in ChangeElement()), but
11596                    also prevent additional element actions for this element */
11597
11598                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11599                     !level.use_action_after_change_bug)
11600                   continue;
11601 #endif
11602
11603
11604 #if 0
11605                 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11606                        element, EL_NAME(element), p);
11607 #endif
11608
11609                 ExecuteCustomElementAction(x, y, element, p);
11610                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11611               }
11612 #else
11613               if (change->has_action)
11614               {
11615                 ExecuteCustomElementAction(x, y, element, p);
11616                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11617               }
11618 #endif
11619             }
11620           }
11621
11622           if (change->can_change)
11623           {
11624             change_done = TRUE;
11625             change_done_any = TRUE;
11626
11627 #if 0
11628             printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11629                    element, EL_NAME(element), p);
11630 #endif
11631
11632           }
11633         }
11634       }
11635     }
11636   }
11637
11638   RECURSION_LOOP_DETECTION_END();
11639
11640   return change_done_any;
11641 }
11642
11643 static boolean CheckElementChangeExt(int x, int y,
11644                                      int element,
11645                                      int trigger_element,
11646                                      int trigger_event,
11647                                      int trigger_player,
11648                                      int trigger_side)
11649 {
11650   boolean change_done = FALSE;
11651   int p;
11652
11653   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11654       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11655     return FALSE;
11656
11657   if (Feld[x][y] == EL_BLOCKED)
11658   {
11659     Blocked2Moving(x, y, &x, &y);
11660     element = Feld[x][y];
11661   }
11662
11663 #if 0
11664   /* check if element has already changed */
11665   if (Feld[x][y] != element)
11666     return FALSE;
11667 #else
11668   /* check if element has already changed or is about to change after moving */
11669   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11670        Feld[x][y] != element) ||
11671
11672       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11673        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11674         ChangePage[x][y] != -1)))
11675     return FALSE;
11676 #endif
11677
11678 #if 0
11679   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11680          trigger_event, recursion_loop_depth, recursion_loop_detected,
11681          recursion_loop_element, EL_NAME(recursion_loop_element));
11682 #endif
11683
11684   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11685
11686 #if 0
11687   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11688 #endif
11689
11690   for (p = 0; p < element_info[element].num_change_pages; p++)
11691   {
11692     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11693
11694     /* check trigger element for all events where the element that is checked
11695        for changing interacts with a directly adjacent element -- this is
11696        different to element changes that affect other elements to change on the
11697        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11698     boolean check_trigger_element =
11699       (trigger_event == CE_TOUCHING_X ||
11700        trigger_event == CE_HITTING_X ||
11701        trigger_event == CE_HIT_BY_X ||
11702 #if 1
11703        /* this one was forgotten until 3.2.3 */
11704        trigger_event == CE_DIGGING_X);
11705 #endif
11706
11707     if (change->can_change_or_has_action &&
11708         change->has_event[trigger_event] &&
11709         change->trigger_side & trigger_side &&
11710         change->trigger_player & trigger_player &&
11711         (!check_trigger_element ||
11712          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11713     {
11714       change->actual_trigger_element = trigger_element;
11715       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11716       change->actual_trigger_player_bits = trigger_player;
11717       change->actual_trigger_side = trigger_side;
11718       change->actual_trigger_ce_value = CustomValue[x][y];
11719       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11720
11721       /* special case: trigger element not at (x,y) position for some events */
11722       if (check_trigger_element)
11723       {
11724         static struct
11725         {
11726           int dx, dy;
11727         } move_xy[] =
11728           {
11729             {  0,  0 },
11730             { -1,  0 },
11731             { +1,  0 },
11732             {  0,  0 },
11733             {  0, -1 },
11734             {  0,  0 }, { 0, 0 }, { 0, 0 },
11735             {  0, +1 }
11736           };
11737
11738         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11739         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11740
11741         change->actual_trigger_ce_value = CustomValue[xx][yy];
11742         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11743       }
11744
11745       if (change->can_change && !change_done)
11746       {
11747         ChangeDelay[x][y] = 1;
11748         ChangeEvent[x][y] = trigger_event;
11749
11750         HandleElementChange(x, y, p);
11751
11752         change_done = TRUE;
11753       }
11754 #if USE_NEW_DELAYED_ACTION
11755       else if (change->has_action)
11756       {
11757         ExecuteCustomElementAction(x, y, element, p);
11758         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11759       }
11760 #else
11761       if (change->has_action)
11762       {
11763         ExecuteCustomElementAction(x, y, element, p);
11764         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11765       }
11766 #endif
11767     }
11768   }
11769
11770   RECURSION_LOOP_DETECTION_END();
11771
11772   return change_done;
11773 }
11774
11775 static void PlayPlayerSound(struct PlayerInfo *player)
11776 {
11777   int jx = player->jx, jy = player->jy;
11778   int sound_element = player->artwork_element;
11779   int last_action = player->last_action_waiting;
11780   int action = player->action_waiting;
11781
11782   if (player->is_waiting)
11783   {
11784     if (action != last_action)
11785       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11786     else
11787       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11788   }
11789   else
11790   {
11791     if (action != last_action)
11792       StopSound(element_info[sound_element].sound[last_action]);
11793
11794     if (last_action == ACTION_SLEEPING)
11795       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11796   }
11797 }
11798
11799 static void PlayAllPlayersSound()
11800 {
11801   int i;
11802
11803   for (i = 0; i < MAX_PLAYERS; i++)
11804     if (stored_player[i].active)
11805       PlayPlayerSound(&stored_player[i]);
11806 }
11807
11808 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11809 {
11810   boolean last_waiting = player->is_waiting;
11811   int move_dir = player->MovDir;
11812
11813   player->dir_waiting = move_dir;
11814   player->last_action_waiting = player->action_waiting;
11815
11816   if (is_waiting)
11817   {
11818     if (!last_waiting)          /* not waiting -> waiting */
11819     {
11820       player->is_waiting = TRUE;
11821
11822       player->frame_counter_bored =
11823         FrameCounter +
11824         game.player_boring_delay_fixed +
11825         GetSimpleRandom(game.player_boring_delay_random);
11826       player->frame_counter_sleeping =
11827         FrameCounter +
11828         game.player_sleeping_delay_fixed +
11829         GetSimpleRandom(game.player_sleeping_delay_random);
11830
11831       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11832     }
11833
11834     if (game.player_sleeping_delay_fixed +
11835         game.player_sleeping_delay_random > 0 &&
11836         player->anim_delay_counter == 0 &&
11837         player->post_delay_counter == 0 &&
11838         FrameCounter >= player->frame_counter_sleeping)
11839       player->is_sleeping = TRUE;
11840     else if (game.player_boring_delay_fixed +
11841              game.player_boring_delay_random > 0 &&
11842              FrameCounter >= player->frame_counter_bored)
11843       player->is_bored = TRUE;
11844
11845     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11846                               player->is_bored ? ACTION_BORING :
11847                               ACTION_WAITING);
11848
11849     if (player->is_sleeping && player->use_murphy)
11850     {
11851       /* special case for sleeping Murphy when leaning against non-free tile */
11852
11853       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11854           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11855            !IS_MOVING(player->jx - 1, player->jy)))
11856         move_dir = MV_LEFT;
11857       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11858                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11859                 !IS_MOVING(player->jx + 1, player->jy)))
11860         move_dir = MV_RIGHT;
11861       else
11862         player->is_sleeping = FALSE;
11863
11864       player->dir_waiting = move_dir;
11865     }
11866
11867     if (player->is_sleeping)
11868     {
11869       if (player->num_special_action_sleeping > 0)
11870       {
11871         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11872         {
11873           int last_special_action = player->special_action_sleeping;
11874           int num_special_action = player->num_special_action_sleeping;
11875           int special_action =
11876             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11877              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11878              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11879              last_special_action + 1 : ACTION_SLEEPING);
11880           int special_graphic =
11881             el_act_dir2img(player->artwork_element, special_action, move_dir);
11882
11883           player->anim_delay_counter =
11884             graphic_info[special_graphic].anim_delay_fixed +
11885             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11886           player->post_delay_counter =
11887             graphic_info[special_graphic].post_delay_fixed +
11888             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11889
11890           player->special_action_sleeping = special_action;
11891         }
11892
11893         if (player->anim_delay_counter > 0)
11894         {
11895           player->action_waiting = player->special_action_sleeping;
11896           player->anim_delay_counter--;
11897         }
11898         else if (player->post_delay_counter > 0)
11899         {
11900           player->post_delay_counter--;
11901         }
11902       }
11903     }
11904     else if (player->is_bored)
11905     {
11906       if (player->num_special_action_bored > 0)
11907       {
11908         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11909         {
11910           int special_action =
11911             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11912           int special_graphic =
11913             el_act_dir2img(player->artwork_element, special_action, move_dir);
11914
11915           player->anim_delay_counter =
11916             graphic_info[special_graphic].anim_delay_fixed +
11917             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11918           player->post_delay_counter =
11919             graphic_info[special_graphic].post_delay_fixed +
11920             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11921
11922           player->special_action_bored = special_action;
11923         }
11924
11925         if (player->anim_delay_counter > 0)
11926         {
11927           player->action_waiting = player->special_action_bored;
11928           player->anim_delay_counter--;
11929         }
11930         else if (player->post_delay_counter > 0)
11931         {
11932           player->post_delay_counter--;
11933         }
11934       }
11935     }
11936   }
11937   else if (last_waiting)        /* waiting -> not waiting */
11938   {
11939     player->is_waiting = FALSE;
11940     player->is_bored = FALSE;
11941     player->is_sleeping = FALSE;
11942
11943     player->frame_counter_bored = -1;
11944     player->frame_counter_sleeping = -1;
11945
11946     player->anim_delay_counter = 0;
11947     player->post_delay_counter = 0;
11948
11949     player->dir_waiting = player->MovDir;
11950     player->action_waiting = ACTION_DEFAULT;
11951
11952     player->special_action_bored = ACTION_DEFAULT;
11953     player->special_action_sleeping = ACTION_DEFAULT;
11954   }
11955 }
11956
11957 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11958 {
11959   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11960   int left      = player_action & JOY_LEFT;
11961   int right     = player_action & JOY_RIGHT;
11962   int up        = player_action & JOY_UP;
11963   int down      = player_action & JOY_DOWN;
11964   int button1   = player_action & JOY_BUTTON_1;
11965   int button2   = player_action & JOY_BUTTON_2;
11966   int dx        = (left ? -1 : right ? 1 : 0);
11967   int dy        = (up   ? -1 : down  ? 1 : 0);
11968
11969   if (!player->active || tape.pausing)
11970     return 0;
11971
11972   if (player_action)
11973   {
11974     if (button1)
11975       snapped = SnapField(player, dx, dy);
11976     else
11977     {
11978       if (button2)
11979         dropped = DropElement(player);
11980
11981       moved = MovePlayer(player, dx, dy);
11982     }
11983
11984     if (tape.single_step && tape.recording && !tape.pausing)
11985     {
11986       if (button1 || (dropped && !moved))
11987       {
11988         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11989         SnapField(player, 0, 0);                /* stop snapping */
11990       }
11991     }
11992
11993     SetPlayerWaiting(player, FALSE);
11994
11995     return player_action;
11996   }
11997   else
11998   {
11999     /* no actions for this player (no input at player's configured device) */
12000
12001     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12002     SnapField(player, 0, 0);
12003     CheckGravityMovementWhenNotMoving(player);
12004
12005     if (player->MovPos == 0)
12006       SetPlayerWaiting(player, TRUE);
12007
12008     if (player->MovPos == 0)    /* needed for tape.playing */
12009       player->is_moving = FALSE;
12010
12011     player->is_dropping = FALSE;
12012     player->is_dropping_pressed = FALSE;
12013     player->drop_pressed_delay = 0;
12014
12015     return 0;
12016   }
12017 }
12018
12019 static void CheckLevelTime()
12020 {
12021   int i;
12022
12023   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12024   {
12025     if (level.native_em_level->lev->home == 0)  /* all players at home */
12026     {
12027       PlayerWins(local_player);
12028
12029       AllPlayersGone = TRUE;
12030
12031       level.native_em_level->lev->home = -1;
12032     }
12033
12034     if (level.native_em_level->ply[0]->alive == 0 &&
12035         level.native_em_level->ply[1]->alive == 0 &&
12036         level.native_em_level->ply[2]->alive == 0 &&
12037         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12038       AllPlayersGone = TRUE;
12039   }
12040
12041   if (TimeFrames >= FRAMES_PER_SECOND)
12042   {
12043     TimeFrames = 0;
12044     TapeTime++;
12045
12046     for (i = 0; i < MAX_PLAYERS; i++)
12047     {
12048       struct PlayerInfo *player = &stored_player[i];
12049
12050       if (SHIELD_ON(player))
12051       {
12052         player->shield_normal_time_left--;
12053
12054         if (player->shield_deadly_time_left > 0)
12055           player->shield_deadly_time_left--;
12056       }
12057     }
12058
12059     if (!local_player->LevelSolved && !level.use_step_counter)
12060     {
12061       TimePlayed++;
12062
12063       if (TimeLeft > 0)
12064       {
12065         TimeLeft--;
12066
12067         if (TimeLeft <= 10 && setup.time_limit)
12068           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12069
12070 #if 1
12071         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12072
12073         DisplayGameControlValues();
12074 #else
12075         DrawGameValue_Time(TimeLeft);
12076 #endif
12077
12078         if (!TimeLeft && setup.time_limit)
12079         {
12080           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12081             level.native_em_level->lev->killed_out_of_time = TRUE;
12082           else
12083             for (i = 0; i < MAX_PLAYERS; i++)
12084               KillPlayer(&stored_player[i]);
12085         }
12086       }
12087 #if 1
12088       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12089       {
12090         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12091
12092         DisplayGameControlValues();
12093       }
12094 #else
12095       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12096         DrawGameValue_Time(TimePlayed);
12097 #endif
12098
12099       level.native_em_level->lev->time =
12100         (level.time == 0 ? TimePlayed : TimeLeft);
12101     }
12102
12103     if (tape.recording || tape.playing)
12104       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12105   }
12106
12107 #if 1
12108   UpdateAndDisplayGameControlValues();
12109 #else
12110   UpdateGameDoorValues();
12111   DrawGameDoorValues();
12112 #endif
12113 }
12114
12115 void AdvanceFrameAndPlayerCounters(int player_nr)
12116 {
12117   int i;
12118
12119   /* advance frame counters (global frame counter and time frame counter) */
12120   FrameCounter++;
12121   TimeFrames++;
12122
12123   /* advance player counters (counters for move delay, move animation etc.) */
12124   for (i = 0; i < MAX_PLAYERS; i++)
12125   {
12126     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12127     int move_delay_value = stored_player[i].move_delay_value;
12128     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12129
12130     if (!advance_player_counters)       /* not all players may be affected */
12131       continue;
12132
12133 #if USE_NEW_PLAYER_ANIM
12134     if (move_frames == 0)       /* less than one move per game frame */
12135     {
12136       int stepsize = TILEX / move_delay_value;
12137       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12138       int count = (stored_player[i].is_moving ?
12139                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12140
12141       if (count % delay == 0)
12142         move_frames = 1;
12143     }
12144 #endif
12145
12146     stored_player[i].Frame += move_frames;
12147
12148     if (stored_player[i].MovPos != 0)
12149       stored_player[i].StepFrame += move_frames;
12150
12151     if (stored_player[i].move_delay > 0)
12152       stored_player[i].move_delay--;
12153
12154     /* due to bugs in previous versions, counter must count up, not down */
12155     if (stored_player[i].push_delay != -1)
12156       stored_player[i].push_delay++;
12157
12158     if (stored_player[i].drop_delay > 0)
12159       stored_player[i].drop_delay--;
12160
12161     if (stored_player[i].is_dropping_pressed)
12162       stored_player[i].drop_pressed_delay++;
12163   }
12164 }
12165
12166 void StartGameActions(boolean init_network_game, boolean record_tape,
12167                       long random_seed)
12168 {
12169   unsigned long new_random_seed = InitRND(random_seed);
12170
12171   if (record_tape)
12172     TapeStartRecording(new_random_seed);
12173
12174 #if defined(NETWORK_AVALIABLE)
12175   if (init_network_game)
12176   {
12177     SendToServer_StartPlaying();
12178
12179     return;
12180   }
12181 #endif
12182
12183   InitGame();
12184 }
12185
12186 void GameActions()
12187 {
12188   static unsigned long game_frame_delay = 0;
12189   unsigned long game_frame_delay_value;
12190   byte *recorded_player_action;
12191   byte summarized_player_action = 0;
12192   byte tape_action[MAX_PLAYERS];
12193   int i;
12194
12195   /* detect endless loops, caused by custom element programming */
12196   if (recursion_loop_detected && recursion_loop_depth == 0)
12197   {
12198     char *message = getStringCat3("Internal Error ! Element ",
12199                                   EL_NAME(recursion_loop_element),
12200                                   " caused endless loop ! Quit the game ?");
12201
12202     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12203           EL_NAME(recursion_loop_element));
12204
12205     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12206
12207     recursion_loop_detected = FALSE;    /* if game should be continued */
12208
12209     free(message);
12210
12211     return;
12212   }
12213
12214   if (game.restart_level)
12215     StartGameActions(options.network, setup.autorecord, level.random_seed);
12216
12217   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12218   {
12219     if (level.native_em_level->lev->home == 0)  /* all players at home */
12220     {
12221       PlayerWins(local_player);
12222
12223       AllPlayersGone = TRUE;
12224
12225       level.native_em_level->lev->home = -1;
12226     }
12227
12228     if (level.native_em_level->ply[0]->alive == 0 &&
12229         level.native_em_level->ply[1]->alive == 0 &&
12230         level.native_em_level->ply[2]->alive == 0 &&
12231         level.native_em_level->ply[3]->alive == 0)      /* all dead */
12232       AllPlayersGone = TRUE;
12233   }
12234
12235   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12236     GameWon();
12237
12238   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12239     TapeStop();
12240
12241   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
12242     return;
12243
12244   game_frame_delay_value =
12245     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12246
12247   if (tape.playing && tape.warp_forward && !tape.pausing)
12248     game_frame_delay_value = 0;
12249
12250   /* ---------- main game synchronization point ---------- */
12251
12252   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12253
12254   if (network_playing && !network_player_action_received)
12255   {
12256     /* try to get network player actions in time */
12257
12258 #if defined(NETWORK_AVALIABLE)
12259     /* last chance to get network player actions without main loop delay */
12260     HandleNetworking();
12261 #endif
12262
12263     /* game was quit by network peer */
12264     if (game_status != GAME_MODE_PLAYING)
12265       return;
12266
12267     if (!network_player_action_received)
12268       return;           /* failed to get network player actions in time */
12269
12270     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12271   }
12272
12273   if (tape.pausing)
12274     return;
12275
12276   /* at this point we know that we really continue executing the game */
12277
12278   network_player_action_received = FALSE;
12279
12280   /* when playing tape, read previously recorded player input from tape data */
12281   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12282
12283 #if 1
12284   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12285   if (tape.pausing)
12286     return;
12287 #endif
12288
12289   if (tape.set_centered_player)
12290   {
12291     game.centered_player_nr_next = tape.centered_player_nr_next;
12292     game.set_centered_player = TRUE;
12293   }
12294
12295   for (i = 0; i < MAX_PLAYERS; i++)
12296   {
12297     summarized_player_action |= stored_player[i].action;
12298
12299     if (!network_playing)
12300       stored_player[i].effective_action = stored_player[i].action;
12301   }
12302
12303 #if defined(NETWORK_AVALIABLE)
12304   if (network_playing)
12305     SendToServer_MovePlayer(summarized_player_action);
12306 #endif
12307
12308   if (!options.network && !setup.team_mode)
12309     local_player->effective_action = summarized_player_action;
12310
12311   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12312   {
12313     for (i = 0; i < MAX_PLAYERS; i++)
12314       stored_player[i].effective_action =
12315         (i == game.centered_player_nr ? summarized_player_action : 0);
12316   }
12317
12318   if (recorded_player_action != NULL)
12319     for (i = 0; i < MAX_PLAYERS; i++)
12320       stored_player[i].effective_action = recorded_player_action[i];
12321
12322   for (i = 0; i < MAX_PLAYERS; i++)
12323   {
12324     tape_action[i] = stored_player[i].effective_action;
12325
12326     /* (this can only happen in the R'n'D game engine) */
12327     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12328       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12329   }
12330
12331   /* only record actions from input devices, but not programmed actions */
12332   if (tape.recording)
12333     TapeRecordAction(tape_action);
12334
12335 #if USE_NEW_PLAYER_ASSIGNMENTS
12336   {
12337     byte mapped_action[MAX_PLAYERS];
12338
12339     for (i = 0; i < MAX_PLAYERS; i++)
12340       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12341
12342     for (i = 0; i < MAX_PLAYERS; i++)
12343       stored_player[i].effective_action = mapped_action[i];
12344   }
12345 #endif
12346
12347   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12348   {
12349     GameActions_EM_Main();
12350   }
12351   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12352   {
12353     GameActions_SP_Main();
12354   }
12355   else
12356   {
12357     GameActions_RND();
12358   }
12359 }
12360
12361 void GameActions_EM_Main()
12362 {
12363   byte effective_action[MAX_PLAYERS];
12364   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12365   int i;
12366
12367   for (i = 0; i < MAX_PLAYERS; i++)
12368     effective_action[i] = stored_player[i].effective_action;
12369
12370   GameActions_EM(effective_action, warp_mode);
12371
12372   CheckLevelTime();
12373
12374   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12375 }
12376
12377 void GameActions_SP_Main()
12378 {
12379   byte effective_action[MAX_PLAYERS];
12380   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12381   int i;
12382
12383   for (i = 0; i < MAX_PLAYERS; i++)
12384     effective_action[i] = stored_player[i].effective_action;
12385
12386   GameActions_SP(effective_action, warp_mode);
12387
12388   CheckLevelTime();
12389
12390   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12391 }
12392
12393 void GameActions_RND()
12394 {
12395   int magic_wall_x = 0, magic_wall_y = 0;
12396   int i, x, y, element, graphic;
12397
12398   InitPlayfieldScanModeVars();
12399
12400 #if USE_ONE_MORE_CHANGE_PER_FRAME
12401   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12402   {
12403     SCAN_PLAYFIELD(x, y)
12404     {
12405       ChangeCount[x][y] = 0;
12406       ChangeEvent[x][y] = -1;
12407     }
12408   }
12409 #endif
12410
12411   if (game.set_centered_player)
12412   {
12413     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12414
12415     /* switching to "all players" only possible if all players fit to screen */
12416     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12417     {
12418       game.centered_player_nr_next = game.centered_player_nr;
12419       game.set_centered_player = FALSE;
12420     }
12421
12422     /* do not switch focus to non-existing (or non-active) player */
12423     if (game.centered_player_nr_next >= 0 &&
12424         !stored_player[game.centered_player_nr_next].active)
12425     {
12426       game.centered_player_nr_next = game.centered_player_nr;
12427       game.set_centered_player = FALSE;
12428     }
12429   }
12430
12431   if (game.set_centered_player &&
12432       ScreenMovPos == 0)        /* screen currently aligned at tile position */
12433   {
12434     int sx, sy;
12435
12436     if (game.centered_player_nr_next == -1)
12437     {
12438       setScreenCenteredToAllPlayers(&sx, &sy);
12439     }
12440     else
12441     {
12442       sx = stored_player[game.centered_player_nr_next].jx;
12443       sy = stored_player[game.centered_player_nr_next].jy;
12444     }
12445
12446     game.centered_player_nr = game.centered_player_nr_next;
12447     game.set_centered_player = FALSE;
12448
12449     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12450     DrawGameDoorValues();
12451   }
12452
12453   for (i = 0; i < MAX_PLAYERS; i++)
12454   {
12455     int actual_player_action = stored_player[i].effective_action;
12456
12457 #if 1
12458     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12459        - rnd_equinox_tetrachloride 048
12460        - rnd_equinox_tetrachloride_ii 096
12461        - rnd_emanuel_schmieg 002
12462        - doctor_sloan_ww 001, 020
12463     */
12464     if (stored_player[i].MovPos == 0)
12465       CheckGravityMovement(&stored_player[i]);
12466 #endif
12467
12468     /* overwrite programmed action with tape action */
12469     if (stored_player[i].programmed_action)
12470       actual_player_action = stored_player[i].programmed_action;
12471
12472     PlayerActions(&stored_player[i], actual_player_action);
12473
12474     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12475   }
12476
12477   ScrollScreen(NULL, SCROLL_GO_ON);
12478
12479   /* for backwards compatibility, the following code emulates a fixed bug that
12480      occured when pushing elements (causing elements that just made their last
12481      pushing step to already (if possible) make their first falling step in the
12482      same game frame, which is bad); this code is also needed to use the famous
12483      "spring push bug" which is used in older levels and might be wanted to be
12484      used also in newer levels, but in this case the buggy pushing code is only
12485      affecting the "spring" element and no other elements */
12486
12487   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12488   {
12489     for (i = 0; i < MAX_PLAYERS; i++)
12490     {
12491       struct PlayerInfo *player = &stored_player[i];
12492       int x = player->jx;
12493       int y = player->jy;
12494
12495       if (player->active && player->is_pushing && player->is_moving &&
12496           IS_MOVING(x, y) &&
12497           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12498            Feld[x][y] == EL_SPRING))
12499       {
12500         ContinueMoving(x, y);
12501
12502         /* continue moving after pushing (this is actually a bug) */
12503         if (!IS_MOVING(x, y))
12504           Stop[x][y] = FALSE;
12505       }
12506     }
12507   }
12508
12509 #if 0
12510   debug_print_timestamp(0, "start main loop profiling");
12511 #endif
12512
12513   SCAN_PLAYFIELD(x, y)
12514   {
12515     ChangeCount[x][y] = 0;
12516     ChangeEvent[x][y] = -1;
12517
12518     /* this must be handled before main playfield loop */
12519     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12520     {
12521       MovDelay[x][y]--;
12522       if (MovDelay[x][y] <= 0)
12523         RemoveField(x, y);
12524     }
12525
12526 #if USE_NEW_SNAP_DELAY
12527     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12528     {
12529       MovDelay[x][y]--;
12530       if (MovDelay[x][y] <= 0)
12531       {
12532         RemoveField(x, y);
12533         TEST_DrawLevelField(x, y);
12534
12535         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12536       }
12537     }
12538 #endif
12539
12540 #if DEBUG
12541     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12542     {
12543       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12544       printf("GameActions(): This should never happen!\n");
12545
12546       ChangePage[x][y] = -1;
12547     }
12548 #endif
12549
12550     Stop[x][y] = FALSE;
12551     if (WasJustMoving[x][y] > 0)
12552       WasJustMoving[x][y]--;
12553     if (WasJustFalling[x][y] > 0)
12554       WasJustFalling[x][y]--;
12555     if (CheckCollision[x][y] > 0)
12556       CheckCollision[x][y]--;
12557     if (CheckImpact[x][y] > 0)
12558       CheckImpact[x][y]--;
12559
12560     GfxFrame[x][y]++;
12561
12562     /* reset finished pushing action (not done in ContinueMoving() to allow
12563        continuous pushing animation for elements with zero push delay) */
12564     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12565     {
12566       ResetGfxAnimation(x, y);
12567       TEST_DrawLevelField(x, y);
12568     }
12569
12570 #if DEBUG
12571     if (IS_BLOCKED(x, y))
12572     {
12573       int oldx, oldy;
12574
12575       Blocked2Moving(x, y, &oldx, &oldy);
12576       if (!IS_MOVING(oldx, oldy))
12577       {
12578         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12579         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12580         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12581         printf("GameActions(): This should never happen!\n");
12582       }
12583     }
12584 #endif
12585   }
12586
12587 #if 0
12588   debug_print_timestamp(0, "- time for pre-main loop:");
12589 #endif
12590
12591 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
12592   SCAN_PLAYFIELD(x, y)
12593   {
12594     element = Feld[x][y];
12595     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12596
12597 #if 1
12598     {
12599 #if 1
12600       int element2 = element;
12601       int graphic2 = graphic;
12602 #else
12603       int element2 = Feld[x][y];
12604       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12605 #endif
12606       int last_gfx_frame = GfxFrame[x][y];
12607
12608       if (graphic_info[graphic2].anim_global_sync)
12609         GfxFrame[x][y] = FrameCounter;
12610       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12611         GfxFrame[x][y] = CustomValue[x][y];
12612       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12613         GfxFrame[x][y] = element_info[element2].collect_score;
12614       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12615         GfxFrame[x][y] = ChangeDelay[x][y];
12616
12617       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12618         DrawLevelGraphicAnimation(x, y, graphic2);
12619     }
12620 #else
12621     ResetGfxFrame(x, y, TRUE);
12622 #endif
12623
12624 #if 1
12625     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12626         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12627       ResetRandomAnimationValue(x, y);
12628 #endif
12629
12630 #if 1
12631     SetRandomAnimationValue(x, y);
12632 #endif
12633
12634 #if 1
12635     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12636 #endif
12637   }
12638 #endif  // -------------------- !!! TEST ONLY !!! --------------------
12639
12640 #if 0
12641   debug_print_timestamp(0, "- time for TEST loop:     -->");
12642 #endif
12643
12644   SCAN_PLAYFIELD(x, y)
12645   {
12646     element = Feld[x][y];
12647     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12648
12649     ResetGfxFrame(x, y, TRUE);
12650
12651     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12652         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12653       ResetRandomAnimationValue(x, y);
12654
12655     SetRandomAnimationValue(x, y);
12656
12657     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12658
12659     if (IS_INACTIVE(element))
12660     {
12661       if (IS_ANIMATED(graphic))
12662         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12663
12664       continue;
12665     }
12666
12667     /* this may take place after moving, so 'element' may have changed */
12668     if (IS_CHANGING(x, y) &&
12669         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12670     {
12671       int page = element_info[element].event_page_nr[CE_DELAY];
12672
12673 #if 1
12674       HandleElementChange(x, y, page);
12675 #else
12676       if (CAN_CHANGE(element))
12677         HandleElementChange(x, y, page);
12678
12679       if (HAS_ACTION(element))
12680         ExecuteCustomElementAction(x, y, element, page);
12681 #endif
12682
12683       element = Feld[x][y];
12684       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12685     }
12686
12687 #if 0   // ---------------------------------------------------------------------
12688
12689     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12690     {
12691       StartMoving(x, y);
12692
12693       element = Feld[x][y];
12694       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12695
12696       if (IS_ANIMATED(graphic) &&
12697           !IS_MOVING(x, y) &&
12698           !Stop[x][y])
12699         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12700
12701       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12702         TEST_DrawTwinkleOnField(x, y);
12703     }
12704     else if (IS_MOVING(x, y))
12705       ContinueMoving(x, y);
12706     else
12707     {
12708       switch (element)
12709       {
12710         case EL_ACID:
12711         case EL_EXIT_OPEN:
12712         case EL_EM_EXIT_OPEN:
12713         case EL_SP_EXIT_OPEN:
12714         case EL_STEEL_EXIT_OPEN:
12715         case EL_EM_STEEL_EXIT_OPEN:
12716         case EL_SP_TERMINAL:
12717         case EL_SP_TERMINAL_ACTIVE:
12718         case EL_EXTRA_TIME:
12719         case EL_SHIELD_NORMAL:
12720         case EL_SHIELD_DEADLY:
12721           if (IS_ANIMATED(graphic))
12722             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12723           break;
12724
12725         case EL_DYNAMITE_ACTIVE:
12726         case EL_EM_DYNAMITE_ACTIVE:
12727         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12728         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12729         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12730         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12731         case EL_SP_DISK_RED_ACTIVE:
12732           CheckDynamite(x, y);
12733           break;
12734
12735         case EL_AMOEBA_GROWING:
12736           AmoebeWaechst(x, y);
12737           break;
12738
12739         case EL_AMOEBA_SHRINKING:
12740           AmoebaDisappearing(x, y);
12741           break;
12742
12743 #if !USE_NEW_AMOEBA_CODE
12744         case EL_AMOEBA_WET:
12745         case EL_AMOEBA_DRY:
12746         case EL_AMOEBA_FULL:
12747         case EL_BD_AMOEBA:
12748         case EL_EMC_DRIPPER:
12749           AmoebeAbleger(x, y);
12750           break;
12751 #endif
12752
12753         case EL_GAME_OF_LIFE:
12754         case EL_BIOMAZE:
12755           Life(x, y);
12756           break;
12757
12758         case EL_EXIT_CLOSED:
12759           CheckExit(x, y);
12760           break;
12761
12762         case EL_EM_EXIT_CLOSED:
12763           CheckExitEM(x, y);
12764           break;
12765
12766         case EL_STEEL_EXIT_CLOSED:
12767           CheckExitSteel(x, y);
12768           break;
12769
12770         case EL_EM_STEEL_EXIT_CLOSED:
12771           CheckExitSteelEM(x, y);
12772           break;
12773
12774         case EL_SP_EXIT_CLOSED:
12775           CheckExitSP(x, y);
12776           break;
12777
12778         case EL_EXPANDABLE_WALL_GROWING:
12779         case EL_EXPANDABLE_STEELWALL_GROWING:
12780           MauerWaechst(x, y);
12781           break;
12782
12783         case EL_EXPANDABLE_WALL:
12784         case EL_EXPANDABLE_WALL_HORIZONTAL:
12785         case EL_EXPANDABLE_WALL_VERTICAL:
12786         case EL_EXPANDABLE_WALL_ANY:
12787         case EL_BD_EXPANDABLE_WALL:
12788           MauerAbleger(x, y);
12789           break;
12790
12791         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12792         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12793         case EL_EXPANDABLE_STEELWALL_ANY:
12794           MauerAblegerStahl(x, y);
12795           break;
12796
12797         case EL_FLAMES:
12798           CheckForDragon(x, y);
12799           break;
12800
12801         case EL_EXPLOSION:
12802           break;
12803
12804         case EL_ELEMENT_SNAPPING:
12805         case EL_DIAGONAL_SHRINKING:
12806         case EL_DIAGONAL_GROWING:
12807         {
12808           graphic =
12809             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12810
12811           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12812           break;
12813         }
12814
12815         default:
12816           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12817             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12818           break;
12819       }
12820     }
12821
12822 #else   // ---------------------------------------------------------------------
12823
12824     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12825     {
12826       StartMoving(x, y);
12827
12828       element = Feld[x][y];
12829       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12830
12831       if (IS_ANIMATED(graphic) &&
12832           !IS_MOVING(x, y) &&
12833           !Stop[x][y])
12834         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12835
12836       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12837         TEST_DrawTwinkleOnField(x, y);
12838     }
12839     else if ((element == EL_ACID ||
12840               element == EL_EXIT_OPEN ||
12841               element == EL_EM_EXIT_OPEN ||
12842               element == EL_SP_EXIT_OPEN ||
12843               element == EL_STEEL_EXIT_OPEN ||
12844               element == EL_EM_STEEL_EXIT_OPEN ||
12845               element == EL_SP_TERMINAL ||
12846               element == EL_SP_TERMINAL_ACTIVE ||
12847               element == EL_EXTRA_TIME ||
12848               element == EL_SHIELD_NORMAL ||
12849               element == EL_SHIELD_DEADLY) &&
12850              IS_ANIMATED(graphic))
12851       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12852     else if (IS_MOVING(x, y))
12853       ContinueMoving(x, y);
12854     else if (IS_ACTIVE_BOMB(element))
12855       CheckDynamite(x, y);
12856     else if (element == EL_AMOEBA_GROWING)
12857       AmoebeWaechst(x, y);
12858     else if (element == EL_AMOEBA_SHRINKING)
12859       AmoebaDisappearing(x, y);
12860
12861 #if !USE_NEW_AMOEBA_CODE
12862     else if (IS_AMOEBALIVE(element))
12863       AmoebeAbleger(x, y);
12864 #endif
12865
12866     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12867       Life(x, y);
12868     else if (element == EL_EXIT_CLOSED)
12869       CheckExit(x, y);
12870     else if (element == EL_EM_EXIT_CLOSED)
12871       CheckExitEM(x, y);
12872     else if (element == EL_STEEL_EXIT_CLOSED)
12873       CheckExitSteel(x, y);
12874     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12875       CheckExitSteelEM(x, y);
12876     else if (element == EL_SP_EXIT_CLOSED)
12877       CheckExitSP(x, y);
12878     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12879              element == EL_EXPANDABLE_STEELWALL_GROWING)
12880       MauerWaechst(x, y);
12881     else if (element == EL_EXPANDABLE_WALL ||
12882              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12883              element == EL_EXPANDABLE_WALL_VERTICAL ||
12884              element == EL_EXPANDABLE_WALL_ANY ||
12885              element == EL_BD_EXPANDABLE_WALL)
12886       MauerAbleger(x, y);
12887     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12888              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12889              element == EL_EXPANDABLE_STEELWALL_ANY)
12890       MauerAblegerStahl(x, y);
12891     else if (element == EL_FLAMES)
12892       CheckForDragon(x, y);
12893     else if (element == EL_EXPLOSION)
12894       ; /* drawing of correct explosion animation is handled separately */
12895     else if (element == EL_ELEMENT_SNAPPING ||
12896              element == EL_DIAGONAL_SHRINKING ||
12897              element == EL_DIAGONAL_GROWING)
12898     {
12899       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12900
12901       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12902     }
12903     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12904       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12905
12906 #endif  // ---------------------------------------------------------------------
12907
12908     if (IS_BELT_ACTIVE(element))
12909       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12910
12911     if (game.magic_wall_active)
12912     {
12913       int jx = local_player->jx, jy = local_player->jy;
12914
12915       /* play the element sound at the position nearest to the player */
12916       if ((element == EL_MAGIC_WALL_FULL ||
12917            element == EL_MAGIC_WALL_ACTIVE ||
12918            element == EL_MAGIC_WALL_EMPTYING ||
12919            element == EL_BD_MAGIC_WALL_FULL ||
12920            element == EL_BD_MAGIC_WALL_ACTIVE ||
12921            element == EL_BD_MAGIC_WALL_EMPTYING ||
12922            element == EL_DC_MAGIC_WALL_FULL ||
12923            element == EL_DC_MAGIC_WALL_ACTIVE ||
12924            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12925           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12926       {
12927         magic_wall_x = x;
12928         magic_wall_y = y;
12929       }
12930     }
12931   }
12932
12933 #if 0
12934   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12935 #endif
12936
12937 #if USE_NEW_AMOEBA_CODE
12938   /* new experimental amoeba growth stuff */
12939   if (!(FrameCounter % 8))
12940   {
12941     static unsigned long random = 1684108901;
12942
12943     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12944     {
12945       x = RND(lev_fieldx);
12946       y = RND(lev_fieldy);
12947       element = Feld[x][y];
12948
12949       if (!IS_PLAYER(x,y) &&
12950           (element == EL_EMPTY ||
12951            CAN_GROW_INTO(element) ||
12952            element == EL_QUICKSAND_EMPTY ||
12953            element == EL_QUICKSAND_FAST_EMPTY ||
12954            element == EL_ACID_SPLASH_LEFT ||
12955            element == EL_ACID_SPLASH_RIGHT))
12956       {
12957         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12958             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12959             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12960             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12961           Feld[x][y] = EL_AMOEBA_DROP;
12962       }
12963
12964       random = random * 129 + 1;
12965     }
12966   }
12967 #endif
12968
12969 #if 0
12970   if (game.explosions_delayed)
12971 #endif
12972   {
12973     game.explosions_delayed = FALSE;
12974
12975     SCAN_PLAYFIELD(x, y)
12976     {
12977       element = Feld[x][y];
12978
12979       if (ExplodeField[x][y])
12980         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12981       else if (element == EL_EXPLOSION)
12982         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12983
12984       ExplodeField[x][y] = EX_TYPE_NONE;
12985     }
12986
12987     game.explosions_delayed = TRUE;
12988   }
12989
12990   if (game.magic_wall_active)
12991   {
12992     if (!(game.magic_wall_time_left % 4))
12993     {
12994       int element = Feld[magic_wall_x][magic_wall_y];
12995
12996       if (element == EL_BD_MAGIC_WALL_FULL ||
12997           element == EL_BD_MAGIC_WALL_ACTIVE ||
12998           element == EL_BD_MAGIC_WALL_EMPTYING)
12999         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13000       else if (element == EL_DC_MAGIC_WALL_FULL ||
13001                element == EL_DC_MAGIC_WALL_ACTIVE ||
13002                element == EL_DC_MAGIC_WALL_EMPTYING)
13003         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13004       else
13005         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13006     }
13007
13008     if (game.magic_wall_time_left > 0)
13009     {
13010       game.magic_wall_time_left--;
13011
13012       if (!game.magic_wall_time_left)
13013       {
13014         SCAN_PLAYFIELD(x, y)
13015         {
13016           element = Feld[x][y];
13017
13018           if (element == EL_MAGIC_WALL_ACTIVE ||
13019               element == EL_MAGIC_WALL_FULL)
13020           {
13021             Feld[x][y] = EL_MAGIC_WALL_DEAD;
13022             TEST_DrawLevelField(x, y);
13023           }
13024           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13025                    element == EL_BD_MAGIC_WALL_FULL)
13026           {
13027             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13028             TEST_DrawLevelField(x, y);
13029           }
13030           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13031                    element == EL_DC_MAGIC_WALL_FULL)
13032           {
13033             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13034             TEST_DrawLevelField(x, y);
13035           }
13036         }
13037
13038         game.magic_wall_active = FALSE;
13039       }
13040     }
13041   }
13042
13043   if (game.light_time_left > 0)
13044   {
13045     game.light_time_left--;
13046
13047     if (game.light_time_left == 0)
13048       RedrawAllLightSwitchesAndInvisibleElements();
13049   }
13050
13051   if (game.timegate_time_left > 0)
13052   {
13053     game.timegate_time_left--;
13054
13055     if (game.timegate_time_left == 0)
13056       CloseAllOpenTimegates();
13057   }
13058
13059   if (game.lenses_time_left > 0)
13060   {
13061     game.lenses_time_left--;
13062
13063     if (game.lenses_time_left == 0)
13064       RedrawAllInvisibleElementsForLenses();
13065   }
13066
13067   if (game.magnify_time_left > 0)
13068   {
13069     game.magnify_time_left--;
13070
13071     if (game.magnify_time_left == 0)
13072       RedrawAllInvisibleElementsForMagnifier();
13073   }
13074
13075   for (i = 0; i < MAX_PLAYERS; i++)
13076   {
13077     struct PlayerInfo *player = &stored_player[i];
13078
13079     if (SHIELD_ON(player))
13080     {
13081       if (player->shield_deadly_time_left)
13082         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13083       else if (player->shield_normal_time_left)
13084         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13085     }
13086   }
13087
13088 #if USE_DELAYED_GFX_REDRAW
13089   SCAN_PLAYFIELD(x, y)
13090   {
13091 #if 1
13092     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13093 #else
13094     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13095         GfxRedraw[x][y] != GFX_REDRAW_NONE)
13096 #endif
13097     {
13098       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13099          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13100
13101       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13102         DrawLevelField(x, y);
13103
13104       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13105         DrawLevelFieldCrumbledSand(x, y);
13106
13107       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13108         DrawLevelFieldCrumbledSandNeighbours(x, y);
13109
13110       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13111         DrawTwinkleOnField(x, y);
13112     }
13113
13114     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13115   }
13116 #endif
13117
13118   CheckLevelTime();
13119
13120   DrawAllPlayers();
13121   PlayAllPlayersSound();
13122
13123   if (options.debug)                    /* calculate frames per second */
13124   {
13125     static unsigned long fps_counter = 0;
13126     static int fps_frames = 0;
13127     unsigned long fps_delay_ms = Counter() - fps_counter;
13128
13129     fps_frames++;
13130
13131     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
13132     {
13133       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13134
13135       fps_frames = 0;
13136       fps_counter = Counter();
13137     }
13138
13139     redraw_mask |= REDRAW_FPS;
13140   }
13141
13142   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
13143
13144   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13145   {
13146     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13147
13148     local_player->show_envelope = 0;
13149   }
13150
13151 #if 0
13152   debug_print_timestamp(0, "stop main loop profiling ");
13153   printf("----------------------------------------------------------\n");
13154 #endif
13155
13156   /* use random number generator in every frame to make it less predictable */
13157   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13158     RND(1);
13159 }
13160
13161 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13162 {
13163   int min_x = x, min_y = y, max_x = x, max_y = y;
13164   int i;
13165
13166   for (i = 0; i < MAX_PLAYERS; i++)
13167   {
13168     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13169
13170     if (!stored_player[i].active || &stored_player[i] == player)
13171       continue;
13172
13173     min_x = MIN(min_x, jx);
13174     min_y = MIN(min_y, jy);
13175     max_x = MAX(max_x, jx);
13176     max_y = MAX(max_y, jy);
13177   }
13178
13179   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13180 }
13181
13182 static boolean AllPlayersInVisibleScreen()
13183 {
13184   int i;
13185
13186   for (i = 0; i < MAX_PLAYERS; i++)
13187   {
13188     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13189
13190     if (!stored_player[i].active)
13191       continue;
13192
13193     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13194       return FALSE;
13195   }
13196
13197   return TRUE;
13198 }
13199
13200 void ScrollLevel(int dx, int dy)
13201 {
13202 #if 0
13203   /* (directly solved in BlitBitmap() now) */
13204   static Bitmap *bitmap_db_field2 = NULL;
13205   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13206   int x, y;
13207 #else
13208   int x, y;
13209 #endif
13210
13211 #if 0
13212   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13213   /* only horizontal XOR vertical scroll direction allowed */
13214   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13215     return;
13216 #endif
13217
13218 #if 0
13219   /* (directly solved in BlitBitmap() now) */
13220   if (bitmap_db_field2 == NULL)
13221     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13222
13223   /* needed when blitting directly to same bitmap -- should not be needed with
13224      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13225   BlitBitmap(drawto_field, bitmap_db_field2,
13226              FX + TILEX * (dx == -1) - softscroll_offset,
13227              FY + TILEY * (dy == -1) - softscroll_offset,
13228              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13229              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13230              FX + TILEX * (dx == 1) - softscroll_offset,
13231              FY + TILEY * (dy == 1) - softscroll_offset);
13232   BlitBitmap(bitmap_db_field2, drawto_field,
13233              FX + TILEX * (dx == 1) - softscroll_offset,
13234              FY + TILEY * (dy == 1) - softscroll_offset,
13235              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13236              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13237              FX + TILEX * (dx == 1) - softscroll_offset,
13238              FY + TILEY * (dy == 1) - softscroll_offset);
13239
13240 #else
13241
13242 #if 0
13243   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13244   int xsize = (BX2 - BX1 + 1);
13245   int ysize = (BY2 - BY1 + 1);
13246   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13247   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13248   int step  = (start < end ? +1 : -1);
13249
13250   for (i = start; i != end; i += step)
13251   {
13252     BlitBitmap(drawto_field, drawto_field,
13253                FX + TILEX * (dx != 0 ? i + step : 0),
13254                FY + TILEY * (dy != 0 ? i + step : 0),
13255                TILEX * (dx != 0 ? 1 : xsize),
13256                TILEY * (dy != 0 ? 1 : ysize),
13257                FX + TILEX * (dx != 0 ? i : 0),
13258                FY + TILEY * (dy != 0 ? i : 0));
13259   }
13260
13261 #else
13262
13263   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13264
13265   BlitBitmap(drawto_field, drawto_field,
13266              FX + TILEX * (dx == -1) - softscroll_offset,
13267              FY + TILEY * (dy == -1) - softscroll_offset,
13268              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13269              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13270              FX + TILEX * (dx == 1) - softscroll_offset,
13271              FY + TILEY * (dy == 1) - softscroll_offset);
13272 #endif
13273 #endif
13274
13275   if (dx != 0)
13276   {
13277     x = (dx == 1 ? BX1 : BX2);
13278     for (y = BY1; y <= BY2; y++)
13279       DrawScreenField(x, y);
13280   }
13281
13282   if (dy != 0)
13283   {
13284     y = (dy == 1 ? BY1 : BY2);
13285     for (x = BX1; x <= BX2; x++)
13286       DrawScreenField(x, y);
13287   }
13288
13289   redraw_mask |= REDRAW_FIELD;
13290 }
13291
13292 static boolean canFallDown(struct PlayerInfo *player)
13293 {
13294   int jx = player->jx, jy = player->jy;
13295
13296   return (IN_LEV_FIELD(jx, jy + 1) &&
13297           (IS_FREE(jx, jy + 1) ||
13298            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13299           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13300           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13301 }
13302
13303 static boolean canPassField(int x, int y, int move_dir)
13304 {
13305   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13306   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13307   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13308   int nextx = x + dx;
13309   int nexty = y + dy;
13310   int element = Feld[x][y];
13311
13312   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13313           !CAN_MOVE(element) &&
13314           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13315           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13316           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13317 }
13318
13319 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13320 {
13321   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13322   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13323   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13324   int newx = x + dx;
13325   int newy = y + dy;
13326
13327   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13328           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13329           (IS_DIGGABLE(Feld[newx][newy]) ||
13330            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13331            canPassField(newx, newy, move_dir)));
13332 }
13333
13334 static void CheckGravityMovement(struct PlayerInfo *player)
13335 {
13336 #if USE_PLAYER_GRAVITY
13337   if (player->gravity && !player->programmed_action)
13338 #else
13339   if (game.gravity && !player->programmed_action)
13340 #endif
13341   {
13342     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13343     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13344     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13345     int jx = player->jx, jy = player->jy;
13346     boolean player_is_moving_to_valid_field =
13347       (!player_is_snapping &&
13348        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13349         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13350     boolean player_can_fall_down = canFallDown(player);
13351
13352     if (player_can_fall_down &&
13353         !player_is_moving_to_valid_field)
13354       player->programmed_action = MV_DOWN;
13355   }
13356 }
13357
13358 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13359 {
13360   return CheckGravityMovement(player);
13361
13362 #if USE_PLAYER_GRAVITY
13363   if (player->gravity && !player->programmed_action)
13364 #else
13365   if (game.gravity && !player->programmed_action)
13366 #endif
13367   {
13368     int jx = player->jx, jy = player->jy;
13369     boolean field_under_player_is_free =
13370       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13371     boolean player_is_standing_on_valid_field =
13372       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13373        (IS_WALKABLE(Feld[jx][jy]) &&
13374         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13375
13376     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13377       player->programmed_action = MV_DOWN;
13378   }
13379 }
13380
13381 /*
13382   MovePlayerOneStep()
13383   -----------------------------------------------------------------------------
13384   dx, dy:               direction (non-diagonal) to try to move the player to
13385   real_dx, real_dy:     direction as read from input device (can be diagonal)
13386 */
13387
13388 boolean MovePlayerOneStep(struct PlayerInfo *player,
13389                           int dx, int dy, int real_dx, int real_dy)
13390 {
13391   int jx = player->jx, jy = player->jy;
13392   int new_jx = jx + dx, new_jy = jy + dy;
13393 #if !USE_FIXED_DONT_RUN_INTO
13394   int element;
13395 #endif
13396   int can_move;
13397   boolean player_can_move = !player->cannot_move;
13398
13399   if (!player->active || (!dx && !dy))
13400     return MP_NO_ACTION;
13401
13402   player->MovDir = (dx < 0 ? MV_LEFT :
13403                     dx > 0 ? MV_RIGHT :
13404                     dy < 0 ? MV_UP :
13405                     dy > 0 ? MV_DOWN :  MV_NONE);
13406
13407   if (!IN_LEV_FIELD(new_jx, new_jy))
13408     return MP_NO_ACTION;
13409
13410   if (!player_can_move)
13411   {
13412     if (player->MovPos == 0)
13413     {
13414       player->is_moving = FALSE;
13415       player->is_digging = FALSE;
13416       player->is_collecting = FALSE;
13417       player->is_snapping = FALSE;
13418       player->is_pushing = FALSE;
13419     }
13420   }
13421
13422 #if 1
13423   if (!options.network && game.centered_player_nr == -1 &&
13424       !AllPlayersInSight(player, new_jx, new_jy))
13425     return MP_NO_ACTION;
13426 #else
13427   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13428     return MP_NO_ACTION;
13429 #endif
13430
13431 #if !USE_FIXED_DONT_RUN_INTO
13432   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13433
13434   /* (moved to DigField()) */
13435   if (player_can_move && DONT_RUN_INTO(element))
13436   {
13437     if (element == EL_ACID && dx == 0 && dy == 1)
13438     {
13439       SplashAcid(new_jx, new_jy);
13440       Feld[jx][jy] = EL_PLAYER_1;
13441       InitMovingField(jx, jy, MV_DOWN);
13442       Store[jx][jy] = EL_ACID;
13443       ContinueMoving(jx, jy);
13444       BuryPlayer(player);
13445     }
13446     else
13447       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13448
13449     return MP_MOVING;
13450   }
13451 #endif
13452
13453   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13454   if (can_move != MP_MOVING)
13455     return can_move;
13456
13457   /* check if DigField() has caused relocation of the player */
13458   if (player->jx != jx || player->jy != jy)
13459     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13460
13461   StorePlayer[jx][jy] = 0;
13462   player->last_jx = jx;
13463   player->last_jy = jy;
13464   player->jx = new_jx;
13465   player->jy = new_jy;
13466   StorePlayer[new_jx][new_jy] = player->element_nr;
13467
13468   if (player->move_delay_value_next != -1)
13469   {
13470     player->move_delay_value = player->move_delay_value_next;
13471     player->move_delay_value_next = -1;
13472   }
13473
13474   player->MovPos =
13475     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13476
13477   player->step_counter++;
13478
13479   PlayerVisit[jx][jy] = FrameCounter;
13480
13481 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13482   player->is_moving = TRUE;
13483 #endif
13484
13485 #if 1
13486   /* should better be called in MovePlayer(), but this breaks some tapes */
13487   ScrollPlayer(player, SCROLL_INIT);
13488 #endif
13489
13490   return MP_MOVING;
13491 }
13492
13493 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13494 {
13495   int jx = player->jx, jy = player->jy;
13496   int old_jx = jx, old_jy = jy;
13497   int moved = MP_NO_ACTION;
13498
13499   if (!player->active)
13500     return FALSE;
13501
13502   if (!dx && !dy)
13503   {
13504     if (player->MovPos == 0)
13505     {
13506       player->is_moving = FALSE;
13507       player->is_digging = FALSE;
13508       player->is_collecting = FALSE;
13509       player->is_snapping = FALSE;
13510       player->is_pushing = FALSE;
13511     }
13512
13513     return FALSE;
13514   }
13515
13516   if (player->move_delay > 0)
13517     return FALSE;
13518
13519   player->move_delay = -1;              /* set to "uninitialized" value */
13520
13521   /* store if player is automatically moved to next field */
13522   player->is_auto_moving = (player->programmed_action != MV_NONE);
13523
13524   /* remove the last programmed player action */
13525   player->programmed_action = 0;
13526
13527   if (player->MovPos)
13528   {
13529     /* should only happen if pre-1.2 tape recordings are played */
13530     /* this is only for backward compatibility */
13531
13532     int original_move_delay_value = player->move_delay_value;
13533
13534 #if DEBUG
13535     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13536            tape.counter);
13537 #endif
13538
13539     /* scroll remaining steps with finest movement resolution */
13540     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13541
13542     while (player->MovPos)
13543     {
13544       ScrollPlayer(player, SCROLL_GO_ON);
13545       ScrollScreen(NULL, SCROLL_GO_ON);
13546
13547       AdvanceFrameAndPlayerCounters(player->index_nr);
13548
13549       DrawAllPlayers();
13550       BackToFront();
13551     }
13552
13553     player->move_delay_value = original_move_delay_value;
13554   }
13555
13556   player->is_active = FALSE;
13557
13558   if (player->last_move_dir & MV_HORIZONTAL)
13559   {
13560     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13561       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13562   }
13563   else
13564   {
13565     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13566       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13567   }
13568
13569 #if USE_FIXED_BORDER_RUNNING_GFX
13570   if (!moved && !player->is_active)
13571   {
13572     player->is_moving = FALSE;
13573     player->is_digging = FALSE;
13574     player->is_collecting = FALSE;
13575     player->is_snapping = FALSE;
13576     player->is_pushing = FALSE;
13577   }
13578 #endif
13579
13580   jx = player->jx;
13581   jy = player->jy;
13582
13583 #if 1
13584   if (moved & MP_MOVING && !ScreenMovPos &&
13585       (player->index_nr == game.centered_player_nr ||
13586        game.centered_player_nr == -1))
13587 #else
13588   if (moved & MP_MOVING && !ScreenMovPos &&
13589       (player == local_player || !options.network))
13590 #endif
13591   {
13592     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13593     int offset = game.scroll_delay_value;
13594
13595     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13596     {
13597       /* actual player has left the screen -- scroll in that direction */
13598       if (jx != old_jx)         /* player has moved horizontally */
13599         scroll_x += (jx - old_jx);
13600       else                      /* player has moved vertically */
13601         scroll_y += (jy - old_jy);
13602     }
13603     else
13604     {
13605       if (jx != old_jx)         /* player has moved horizontally */
13606       {
13607         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13608             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13609           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13610
13611         /* don't scroll over playfield boundaries */
13612         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13613           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13614
13615         /* don't scroll more than one field at a time */
13616         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13617
13618         /* don't scroll against the player's moving direction */
13619         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13620             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13621           scroll_x = old_scroll_x;
13622       }
13623       else                      /* player has moved vertically */
13624       {
13625         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13626             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13627           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13628
13629         /* don't scroll over playfield boundaries */
13630         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13631           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13632
13633         /* don't scroll more than one field at a time */
13634         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13635
13636         /* don't scroll against the player's moving direction */
13637         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13638             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13639           scroll_y = old_scroll_y;
13640       }
13641     }
13642
13643     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13644     {
13645 #if 1
13646       if (!options.network && game.centered_player_nr == -1 &&
13647           !AllPlayersInVisibleScreen())
13648       {
13649         scroll_x = old_scroll_x;
13650         scroll_y = old_scroll_y;
13651       }
13652       else
13653 #else
13654       if (!options.network && !AllPlayersInVisibleScreen())
13655       {
13656         scroll_x = old_scroll_x;
13657         scroll_y = old_scroll_y;
13658       }
13659       else
13660 #endif
13661       {
13662         ScrollScreen(player, SCROLL_INIT);
13663         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13664       }
13665     }
13666   }
13667
13668   player->StepFrame = 0;
13669
13670   if (moved & MP_MOVING)
13671   {
13672     if (old_jx != jx && old_jy == jy)
13673       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13674     else if (old_jx == jx && old_jy != jy)
13675       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13676
13677     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
13678
13679     player->last_move_dir = player->MovDir;
13680     player->is_moving = TRUE;
13681     player->is_snapping = FALSE;
13682     player->is_switching = FALSE;
13683     player->is_dropping = FALSE;
13684     player->is_dropping_pressed = FALSE;
13685     player->drop_pressed_delay = 0;
13686
13687 #if 0
13688     /* should better be called here than above, but this breaks some tapes */
13689     ScrollPlayer(player, SCROLL_INIT);
13690 #endif
13691   }
13692   else
13693   {
13694     CheckGravityMovementWhenNotMoving(player);
13695
13696     player->is_moving = FALSE;
13697
13698     /* at this point, the player is allowed to move, but cannot move right now
13699        (e.g. because of something blocking the way) -- ensure that the player
13700        is also allowed to move in the next frame (in old versions before 3.1.1,
13701        the player was forced to wait again for eight frames before next try) */
13702
13703     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13704       player->move_delay = 0;   /* allow direct movement in the next frame */
13705   }
13706
13707   if (player->move_delay == -1)         /* not yet initialized by DigField() */
13708     player->move_delay = player->move_delay_value;
13709
13710   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13711   {
13712     TestIfPlayerTouchesBadThing(jx, jy);
13713     TestIfPlayerTouchesCustomElement(jx, jy);
13714   }
13715
13716   if (!player->active)
13717     RemovePlayer(player);
13718
13719   return moved;
13720 }
13721
13722 void ScrollPlayer(struct PlayerInfo *player, int mode)
13723 {
13724   int jx = player->jx, jy = player->jy;
13725   int last_jx = player->last_jx, last_jy = player->last_jy;
13726   int move_stepsize = TILEX / player->move_delay_value;
13727
13728 #if USE_NEW_PLAYER_SPEED
13729   if (!player->active)
13730     return;
13731
13732   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
13733     return;
13734 #else
13735   if (!player->active || player->MovPos == 0)
13736     return;
13737 #endif
13738
13739   if (mode == SCROLL_INIT)
13740   {
13741     player->actual_frame_counter = FrameCounter;
13742     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13743
13744     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13745         Feld[last_jx][last_jy] == EL_EMPTY)
13746     {
13747       int last_field_block_delay = 0;   /* start with no blocking at all */
13748       int block_delay_adjustment = player->block_delay_adjustment;
13749
13750       /* if player blocks last field, add delay for exactly one move */
13751       if (player->block_last_field)
13752       {
13753         last_field_block_delay += player->move_delay_value;
13754
13755         /* when blocking enabled, prevent moving up despite gravity */
13756 #if USE_PLAYER_GRAVITY
13757         if (player->gravity && player->MovDir == MV_UP)
13758           block_delay_adjustment = -1;
13759 #else
13760         if (game.gravity && player->MovDir == MV_UP)
13761           block_delay_adjustment = -1;
13762 #endif
13763       }
13764
13765       /* add block delay adjustment (also possible when not blocking) */
13766       last_field_block_delay += block_delay_adjustment;
13767
13768       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13769       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13770     }
13771
13772 #if USE_NEW_PLAYER_SPEED
13773     if (player->MovPos != 0)    /* player has not yet reached destination */
13774       return;
13775 #else
13776     return;
13777 #endif
13778   }
13779   else if (!FrameReached(&player->actual_frame_counter, 1))
13780     return;
13781
13782 #if USE_NEW_PLAYER_SPEED
13783   if (player->MovPos != 0)
13784   {
13785     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13786     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13787
13788     /* before DrawPlayer() to draw correct player graphic for this case */
13789     if (player->MovPos == 0)
13790       CheckGravityMovement(player);
13791   }
13792 #else
13793   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13794   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13795
13796   /* before DrawPlayer() to draw correct player graphic for this case */
13797   if (player->MovPos == 0)
13798     CheckGravityMovement(player);
13799 #endif
13800
13801   if (player->MovPos == 0)      /* player reached destination field */
13802   {
13803     if (player->move_delay_reset_counter > 0)
13804     {
13805       player->move_delay_reset_counter--;
13806
13807       if (player->move_delay_reset_counter == 0)
13808       {
13809         /* continue with normal speed after quickly moving through gate */
13810         HALVE_PLAYER_SPEED(player);
13811
13812         /* be able to make the next move without delay */
13813         player->move_delay = 0;
13814       }
13815     }
13816
13817     player->last_jx = jx;
13818     player->last_jy = jy;
13819
13820     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13821         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13822 #if 1
13823         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13824 #endif
13825         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13826         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13827 #if 1
13828         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13829 #endif
13830         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13831         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13832     {
13833       DrawPlayer(player);       /* needed here only to cleanup last field */
13834       RemovePlayer(player);
13835
13836       if (local_player->friends_still_needed == 0 ||
13837           IS_SP_ELEMENT(Feld[jx][jy]))
13838         PlayerWins(player);
13839     }
13840
13841     /* this breaks one level: "machine", level 000 */
13842     {
13843       int move_direction = player->MovDir;
13844       int enter_side = MV_DIR_OPPOSITE(move_direction);
13845       int leave_side = move_direction;
13846       int old_jx = last_jx;
13847       int old_jy = last_jy;
13848       int old_element = Feld[old_jx][old_jy];
13849       int new_element = Feld[jx][jy];
13850
13851       if (IS_CUSTOM_ELEMENT(old_element))
13852         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13853                                    CE_LEFT_BY_PLAYER,
13854                                    player->index_bit, leave_side);
13855
13856       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13857                                           CE_PLAYER_LEAVES_X,
13858                                           player->index_bit, leave_side);
13859
13860       if (IS_CUSTOM_ELEMENT(new_element))
13861         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13862                                    player->index_bit, enter_side);
13863
13864       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13865                                           CE_PLAYER_ENTERS_X,
13866                                           player->index_bit, enter_side);
13867
13868 #if USE_FIX_CE_ACTION_WITH_PLAYER
13869       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13870                                         CE_MOVE_OF_X, move_direction);
13871 #else
13872       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13873                                         CE_MOVE_OF_X, move_direction);
13874 #endif
13875     }
13876
13877     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13878     {
13879       TestIfPlayerTouchesBadThing(jx, jy);
13880       TestIfPlayerTouchesCustomElement(jx, jy);
13881
13882       /* needed because pushed element has not yet reached its destination,
13883          so it would trigger a change event at its previous field location */
13884       if (!player->is_pushing)
13885         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13886
13887       if (!player->active)
13888         RemovePlayer(player);
13889     }
13890
13891     if (!local_player->LevelSolved && level.use_step_counter)
13892     {
13893       int i;
13894
13895       TimePlayed++;
13896
13897       if (TimeLeft > 0)
13898       {
13899         TimeLeft--;
13900
13901         if (TimeLeft <= 10 && setup.time_limit)
13902           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13903
13904 #if 1
13905         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13906
13907         DisplayGameControlValues();
13908 #else
13909         DrawGameValue_Time(TimeLeft);
13910 #endif
13911
13912         if (!TimeLeft && setup.time_limit)
13913           for (i = 0; i < MAX_PLAYERS; i++)
13914             KillPlayer(&stored_player[i]);
13915       }
13916 #if 1
13917       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13918       {
13919         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13920
13921         DisplayGameControlValues();
13922       }
13923 #else
13924       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13925         DrawGameValue_Time(TimePlayed);
13926 #endif
13927     }
13928
13929     if (tape.single_step && tape.recording && !tape.pausing &&
13930         !player->programmed_action)
13931       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13932   }
13933 }
13934
13935 void ScrollScreen(struct PlayerInfo *player, int mode)
13936 {
13937   static unsigned long screen_frame_counter = 0;
13938
13939   if (mode == SCROLL_INIT)
13940   {
13941     /* set scrolling step size according to actual player's moving speed */
13942     ScrollStepSize = TILEX / player->move_delay_value;
13943
13944     screen_frame_counter = FrameCounter;
13945     ScreenMovDir = player->MovDir;
13946     ScreenMovPos = player->MovPos;
13947     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13948     return;
13949   }
13950   else if (!FrameReached(&screen_frame_counter, 1))
13951     return;
13952
13953   if (ScreenMovPos)
13954   {
13955     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13956     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13957     redraw_mask |= REDRAW_FIELD;
13958   }
13959   else
13960     ScreenMovDir = MV_NONE;
13961 }
13962
13963 void TestIfPlayerTouchesCustomElement(int x, int y)
13964 {
13965   static int xy[4][2] =
13966   {
13967     { 0, -1 },
13968     { -1, 0 },
13969     { +1, 0 },
13970     { 0, +1 }
13971   };
13972   static int trigger_sides[4][2] =
13973   {
13974     /* center side       border side */
13975     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13976     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13977     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13978     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13979   };
13980   static int touch_dir[4] =
13981   {
13982     MV_LEFT | MV_RIGHT,
13983     MV_UP   | MV_DOWN,
13984     MV_UP   | MV_DOWN,
13985     MV_LEFT | MV_RIGHT
13986   };
13987   int center_element = Feld[x][y];      /* should always be non-moving! */
13988   int i;
13989
13990   for (i = 0; i < NUM_DIRECTIONS; i++)
13991   {
13992     int xx = x + xy[i][0];
13993     int yy = y + xy[i][1];
13994     int center_side = trigger_sides[i][0];
13995     int border_side = trigger_sides[i][1];
13996     int border_element;
13997
13998     if (!IN_LEV_FIELD(xx, yy))
13999       continue;
14000
14001     if (IS_PLAYER(x, y))                /* player found at center element */
14002     {
14003       struct PlayerInfo *player = PLAYERINFO(x, y);
14004
14005       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14006         border_element = Feld[xx][yy];          /* may be moving! */
14007       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14008         border_element = Feld[xx][yy];
14009       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
14010         border_element = MovingOrBlocked2Element(xx, yy);
14011       else
14012         continue;               /* center and border element do not touch */
14013
14014       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14015                                  player->index_bit, border_side);
14016       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14017                                           CE_PLAYER_TOUCHES_X,
14018                                           player->index_bit, border_side);
14019
14020 #if USE_FIX_CE_ACTION_WITH_PLAYER
14021       {
14022         /* use player element that is initially defined in the level playfield,
14023            not the player element that corresponds to the runtime player number
14024            (example: a level that contains EL_PLAYER_3 as the only player would
14025            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14026         int player_element = PLAYERINFO(x, y)->initial_element;
14027
14028         CheckElementChangeBySide(xx, yy, border_element, player_element,
14029                                  CE_TOUCHING_X, border_side);
14030       }
14031 #endif
14032     }
14033     else if (IS_PLAYER(xx, yy))         /* player found at border element */
14034     {
14035       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14036
14037       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14038       {
14039         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14040           continue;             /* center and border element do not touch */
14041       }
14042
14043       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14044                                  player->index_bit, center_side);
14045       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14046                                           CE_PLAYER_TOUCHES_X,
14047                                           player->index_bit, center_side);
14048
14049 #if USE_FIX_CE_ACTION_WITH_PLAYER
14050       {
14051         /* use player element that is initially defined in the level playfield,
14052            not the player element that corresponds to the runtime player number
14053            (example: a level that contains EL_PLAYER_3 as the only player would
14054            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14055         int player_element = PLAYERINFO(xx, yy)->initial_element;
14056
14057         CheckElementChangeBySide(x, y, center_element, player_element,
14058                                  CE_TOUCHING_X, center_side);
14059       }
14060 #endif
14061
14062       break;
14063     }
14064   }
14065 }
14066
14067 #if USE_ELEMENT_TOUCHING_BUGFIX
14068
14069 void TestIfElementTouchesCustomElement(int x, int y)
14070 {
14071   static int xy[4][2] =
14072   {
14073     { 0, -1 },
14074     { -1, 0 },
14075     { +1, 0 },
14076     { 0, +1 }
14077   };
14078   static int trigger_sides[4][2] =
14079   {
14080     /* center side      border side */
14081     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14082     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14083     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14084     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14085   };
14086   static int touch_dir[4] =
14087   {
14088     MV_LEFT | MV_RIGHT,
14089     MV_UP   | MV_DOWN,
14090     MV_UP   | MV_DOWN,
14091     MV_LEFT | MV_RIGHT
14092   };
14093   boolean change_center_element = FALSE;
14094   int center_element = Feld[x][y];      /* should always be non-moving! */
14095   int border_element_old[NUM_DIRECTIONS];
14096   int i;
14097
14098   for (i = 0; i < NUM_DIRECTIONS; i++)
14099   {
14100     int xx = x + xy[i][0];
14101     int yy = y + xy[i][1];
14102     int border_element;
14103
14104     border_element_old[i] = -1;
14105
14106     if (!IN_LEV_FIELD(xx, yy))
14107       continue;
14108
14109     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14110       border_element = Feld[xx][yy];    /* may be moving! */
14111     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14112       border_element = Feld[xx][yy];
14113     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14114       border_element = MovingOrBlocked2Element(xx, yy);
14115     else
14116       continue;                 /* center and border element do not touch */
14117
14118     border_element_old[i] = border_element;
14119   }
14120
14121   for (i = 0; i < NUM_DIRECTIONS; i++)
14122   {
14123     int xx = x + xy[i][0];
14124     int yy = y + xy[i][1];
14125     int center_side = trigger_sides[i][0];
14126     int border_element = border_element_old[i];
14127
14128     if (border_element == -1)
14129       continue;
14130
14131     /* check for change of border element */
14132     CheckElementChangeBySide(xx, yy, border_element, center_element,
14133                              CE_TOUCHING_X, center_side);
14134
14135     /* (center element cannot be player, so we dont have to check this here) */
14136   }
14137
14138   for (i = 0; i < NUM_DIRECTIONS; i++)
14139   {
14140     int xx = x + xy[i][0];
14141     int yy = y + xy[i][1];
14142     int border_side = trigger_sides[i][1];
14143     int border_element = border_element_old[i];
14144
14145     if (border_element == -1)
14146       continue;
14147
14148     /* check for change of center element (but change it only once) */
14149     if (!change_center_element)
14150       change_center_element =
14151         CheckElementChangeBySide(x, y, center_element, border_element,
14152                                  CE_TOUCHING_X, border_side);
14153
14154 #if USE_FIX_CE_ACTION_WITH_PLAYER
14155     if (IS_PLAYER(xx, yy))
14156     {
14157       /* use player element that is initially defined in the level playfield,
14158          not the player element that corresponds to the runtime player number
14159          (example: a level that contains EL_PLAYER_3 as the only player would
14160          incorrectly give EL_PLAYER_1 for "player->element_nr") */
14161       int player_element = PLAYERINFO(xx, yy)->initial_element;
14162
14163       CheckElementChangeBySide(x, y, center_element, player_element,
14164                                CE_TOUCHING_X, border_side);
14165     }
14166 #endif
14167   }
14168 }
14169
14170 #else
14171
14172 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14173 {
14174   static int xy[4][2] =
14175   {
14176     { 0, -1 },
14177     { -1, 0 },
14178     { +1, 0 },
14179     { 0, +1 }
14180   };
14181   static int trigger_sides[4][2] =
14182   {
14183     /* center side      border side */
14184     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
14185     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
14186     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
14187     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
14188   };
14189   static int touch_dir[4] =
14190   {
14191     MV_LEFT | MV_RIGHT,
14192     MV_UP   | MV_DOWN,
14193     MV_UP   | MV_DOWN,
14194     MV_LEFT | MV_RIGHT
14195   };
14196   boolean change_center_element = FALSE;
14197   int center_element = Feld[x][y];      /* should always be non-moving! */
14198   int i;
14199
14200   for (i = 0; i < NUM_DIRECTIONS; i++)
14201   {
14202     int xx = x + xy[i][0];
14203     int yy = y + xy[i][1];
14204     int center_side = trigger_sides[i][0];
14205     int border_side = trigger_sides[i][1];
14206     int border_element;
14207
14208     if (!IN_LEV_FIELD(xx, yy))
14209       continue;
14210
14211     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14212       border_element = Feld[xx][yy];    /* may be moving! */
14213     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14214       border_element = Feld[xx][yy];
14215     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
14216       border_element = MovingOrBlocked2Element(xx, yy);
14217     else
14218       continue;                 /* center and border element do not touch */
14219
14220     /* check for change of center element (but change it only once) */
14221     if (!change_center_element)
14222       change_center_element =
14223         CheckElementChangeBySide(x, y, center_element, border_element,
14224                                  CE_TOUCHING_X, border_side);
14225
14226     /* check for change of border element */
14227     CheckElementChangeBySide(xx, yy, border_element, center_element,
14228                              CE_TOUCHING_X, center_side);
14229   }
14230 }
14231
14232 #endif
14233
14234 void TestIfElementHitsCustomElement(int x, int y, int direction)
14235 {
14236   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14237   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14238   int hitx = x + dx, hity = y + dy;
14239   int hitting_element = Feld[x][y];
14240   int touched_element;
14241
14242   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14243     return;
14244
14245   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14246                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14247
14248   if (IN_LEV_FIELD(hitx, hity))
14249   {
14250     int opposite_direction = MV_DIR_OPPOSITE(direction);
14251     int hitting_side = direction;
14252     int touched_side = opposite_direction;
14253     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14254                           MovDir[hitx][hity] != direction ||
14255                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14256
14257     object_hit = TRUE;
14258
14259     if (object_hit)
14260     {
14261       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14262                                CE_HITTING_X, touched_side);
14263
14264       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14265                                CE_HIT_BY_X, hitting_side);
14266
14267       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14268                                CE_HIT_BY_SOMETHING, opposite_direction);
14269
14270 #if USE_FIX_CE_ACTION_WITH_PLAYER
14271       if (IS_PLAYER(hitx, hity))
14272       {
14273         /* use player element that is initially defined in the level playfield,
14274            not the player element that corresponds to the runtime player number
14275            (example: a level that contains EL_PLAYER_3 as the only player would
14276            incorrectly give EL_PLAYER_1 for "player->element_nr") */
14277         int player_element = PLAYERINFO(hitx, hity)->initial_element;
14278
14279         CheckElementChangeBySide(x, y, hitting_element, player_element,
14280                                  CE_HITTING_X, touched_side);
14281       }
14282 #endif
14283     }
14284   }
14285
14286   /* "hitting something" is also true when hitting the playfield border */
14287   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14288                            CE_HITTING_SOMETHING, direction);
14289 }
14290
14291 #if 0
14292 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14293 {
14294   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14295   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14296   int hitx = x + dx, hity = y + dy;
14297   int hitting_element = Feld[x][y];
14298   int touched_element;
14299 #if 0
14300   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14301                         !IS_FREE(hitx, hity) &&
14302                         (!IS_MOVING(hitx, hity) ||
14303                          MovDir[hitx][hity] != direction ||
14304                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
14305 #endif
14306
14307   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14308     return;
14309
14310 #if 0
14311   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14312     return;
14313 #endif
14314
14315   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14316                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14317
14318   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14319                            EP_CAN_SMASH_EVERYTHING, direction);
14320
14321   if (IN_LEV_FIELD(hitx, hity))
14322   {
14323     int opposite_direction = MV_DIR_OPPOSITE(direction);
14324     int hitting_side = direction;
14325     int touched_side = opposite_direction;
14326 #if 0
14327     int touched_element = MovingOrBlocked2Element(hitx, hity);
14328 #endif
14329 #if 1
14330     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14331                           MovDir[hitx][hity] != direction ||
14332                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
14333
14334     object_hit = TRUE;
14335 #endif
14336
14337     if (object_hit)
14338     {
14339       int i;
14340
14341       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14342                                CE_SMASHED_BY_SOMETHING, opposite_direction);
14343
14344       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14345                                CE_OTHER_IS_SMASHING, touched_side);
14346
14347       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14348                                CE_OTHER_GETS_SMASHED, hitting_side);
14349     }
14350   }
14351 }
14352 #endif
14353
14354 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14355 {
14356   int i, kill_x = -1, kill_y = -1;
14357
14358   int bad_element = -1;
14359   static int test_xy[4][2] =
14360   {
14361     { 0, -1 },
14362     { -1, 0 },
14363     { +1, 0 },
14364     { 0, +1 }
14365   };
14366   static int test_dir[4] =
14367   {
14368     MV_UP,
14369     MV_LEFT,
14370     MV_RIGHT,
14371     MV_DOWN
14372   };
14373
14374   for (i = 0; i < NUM_DIRECTIONS; i++)
14375   {
14376     int test_x, test_y, test_move_dir, test_element;
14377
14378     test_x = good_x + test_xy[i][0];
14379     test_y = good_y + test_xy[i][1];
14380
14381     if (!IN_LEV_FIELD(test_x, test_y))
14382       continue;
14383
14384     test_move_dir =
14385       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14386
14387     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14388
14389     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14390        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14391     */
14392     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14393         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14394     {
14395       kill_x = test_x;
14396       kill_y = test_y;
14397       bad_element = test_element;
14398
14399       break;
14400     }
14401   }
14402
14403   if (kill_x != -1 || kill_y != -1)
14404   {
14405     if (IS_PLAYER(good_x, good_y))
14406     {
14407       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14408
14409       if (player->shield_deadly_time_left > 0 &&
14410           !IS_INDESTRUCTIBLE(bad_element))
14411         Bang(kill_x, kill_y);
14412       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14413         KillPlayer(player);
14414     }
14415     else
14416       Bang(good_x, good_y);
14417   }
14418 }
14419
14420 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14421 {
14422   int i, kill_x = -1, kill_y = -1;
14423   int bad_element = Feld[bad_x][bad_y];
14424   static int test_xy[4][2] =
14425   {
14426     { 0, -1 },
14427     { -1, 0 },
14428     { +1, 0 },
14429     { 0, +1 }
14430   };
14431   static int touch_dir[4] =
14432   {
14433     MV_LEFT | MV_RIGHT,
14434     MV_UP   | MV_DOWN,
14435     MV_UP   | MV_DOWN,
14436     MV_LEFT | MV_RIGHT
14437   };
14438   static int test_dir[4] =
14439   {
14440     MV_UP,
14441     MV_LEFT,
14442     MV_RIGHT,
14443     MV_DOWN
14444   };
14445
14446   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
14447     return;
14448
14449   for (i = 0; i < NUM_DIRECTIONS; i++)
14450   {
14451     int test_x, test_y, test_move_dir, test_element;
14452
14453     test_x = bad_x + test_xy[i][0];
14454     test_y = bad_y + test_xy[i][1];
14455
14456     if (!IN_LEV_FIELD(test_x, test_y))
14457       continue;
14458
14459     test_move_dir =
14460       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14461
14462     test_element = Feld[test_x][test_y];
14463
14464     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14465        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14466     */
14467     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14468         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14469     {
14470       /* good thing is player or penguin that does not move away */
14471       if (IS_PLAYER(test_x, test_y))
14472       {
14473         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14474
14475         if (bad_element == EL_ROBOT && player->is_moving)
14476           continue;     /* robot does not kill player if he is moving */
14477
14478         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14479         {
14480           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14481             continue;           /* center and border element do not touch */
14482         }
14483
14484         kill_x = test_x;
14485         kill_y = test_y;
14486
14487         break;
14488       }
14489       else if (test_element == EL_PENGUIN)
14490       {
14491         kill_x = test_x;
14492         kill_y = test_y;
14493
14494         break;
14495       }
14496     }
14497   }
14498
14499   if (kill_x != -1 || kill_y != -1)
14500   {
14501     if (IS_PLAYER(kill_x, kill_y))
14502     {
14503       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14504
14505       if (player->shield_deadly_time_left > 0 &&
14506           !IS_INDESTRUCTIBLE(bad_element))
14507         Bang(bad_x, bad_y);
14508       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14509         KillPlayer(player);
14510     }
14511     else
14512       Bang(kill_x, kill_y);
14513   }
14514 }
14515
14516 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14517 {
14518   int bad_element = Feld[bad_x][bad_y];
14519   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14520   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14521   int test_x = bad_x + dx, test_y = bad_y + dy;
14522   int test_move_dir, test_element;
14523   int kill_x = -1, kill_y = -1;
14524
14525   if (!IN_LEV_FIELD(test_x, test_y))
14526     return;
14527
14528   test_move_dir =
14529     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14530
14531   test_element = Feld[test_x][test_y];
14532
14533   if (test_move_dir != bad_move_dir)
14534   {
14535     /* good thing can be player or penguin that does not move away */
14536     if (IS_PLAYER(test_x, test_y))
14537     {
14538       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14539
14540       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14541          player as being hit when he is moving towards the bad thing, because
14542          the "get hit by" condition would be lost after the player stops) */
14543       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14544         return;         /* player moves away from bad thing */
14545
14546       kill_x = test_x;
14547       kill_y = test_y;
14548     }
14549     else if (test_element == EL_PENGUIN)
14550     {
14551       kill_x = test_x;
14552       kill_y = test_y;
14553     }
14554   }
14555
14556   if (kill_x != -1 || kill_y != -1)
14557   {
14558     if (IS_PLAYER(kill_x, kill_y))
14559     {
14560       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14561
14562       if (player->shield_deadly_time_left > 0 &&
14563           !IS_INDESTRUCTIBLE(bad_element))
14564         Bang(bad_x, bad_y);
14565       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14566         KillPlayer(player);
14567     }
14568     else
14569       Bang(kill_x, kill_y);
14570   }
14571 }
14572
14573 void TestIfPlayerTouchesBadThing(int x, int y)
14574 {
14575   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14576 }
14577
14578 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14579 {
14580   TestIfGoodThingHitsBadThing(x, y, move_dir);
14581 }
14582
14583 void TestIfBadThingTouchesPlayer(int x, int y)
14584 {
14585   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14586 }
14587
14588 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14589 {
14590   TestIfBadThingHitsGoodThing(x, y, move_dir);
14591 }
14592
14593 void TestIfFriendTouchesBadThing(int x, int y)
14594 {
14595   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14596 }
14597
14598 void TestIfBadThingTouchesFriend(int x, int y)
14599 {
14600   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14601 }
14602
14603 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14604 {
14605   int i, kill_x = bad_x, kill_y = bad_y;
14606   static int xy[4][2] =
14607   {
14608     { 0, -1 },
14609     { -1, 0 },
14610     { +1, 0 },
14611     { 0, +1 }
14612   };
14613
14614   for (i = 0; i < NUM_DIRECTIONS; i++)
14615   {
14616     int x, y, element;
14617
14618     x = bad_x + xy[i][0];
14619     y = bad_y + xy[i][1];
14620     if (!IN_LEV_FIELD(x, y))
14621       continue;
14622
14623     element = Feld[x][y];
14624     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14625         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14626     {
14627       kill_x = x;
14628       kill_y = y;
14629       break;
14630     }
14631   }
14632
14633   if (kill_x != bad_x || kill_y != bad_y)
14634     Bang(bad_x, bad_y);
14635 }
14636
14637 void KillPlayer(struct PlayerInfo *player)
14638 {
14639   int jx = player->jx, jy = player->jy;
14640
14641   if (!player->active)
14642     return;
14643
14644 #if 0
14645   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14646          player->killed, player->active, player->reanimated);
14647 #endif
14648
14649   /* the following code was introduced to prevent an infinite loop when calling
14650      -> Bang()
14651      -> CheckTriggeredElementChangeExt()
14652      -> ExecuteCustomElementAction()
14653      -> KillPlayer()
14654      -> (infinitely repeating the above sequence of function calls)
14655      which occurs when killing the player while having a CE with the setting
14656      "kill player X when explosion of <player X>"; the solution using a new
14657      field "player->killed" was chosen for backwards compatibility, although
14658      clever use of the fields "player->active" etc. would probably also work */
14659 #if 1
14660   if (player->killed)
14661     return;
14662 #endif
14663
14664   player->killed = TRUE;
14665
14666   /* remove accessible field at the player's position */
14667   Feld[jx][jy] = EL_EMPTY;
14668
14669   /* deactivate shield (else Bang()/Explode() would not work right) */
14670   player->shield_normal_time_left = 0;
14671   player->shield_deadly_time_left = 0;
14672
14673 #if 0
14674   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14675          player->killed, player->active, player->reanimated);
14676 #endif
14677
14678   Bang(jx, jy);
14679
14680 #if 0
14681   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14682          player->killed, player->active, player->reanimated);
14683 #endif
14684
14685 #if USE_PLAYER_REANIMATION
14686 #if 1
14687   if (player->reanimated)       /* killed player may have been reanimated */
14688     player->killed = player->reanimated = FALSE;
14689   else
14690     BuryPlayer(player);
14691 #else
14692   if (player->killed)           /* player may have been reanimated */
14693     BuryPlayer(player);
14694 #endif
14695 #else
14696   BuryPlayer(player);
14697 #endif
14698 }
14699
14700 static void KillPlayerUnlessEnemyProtected(int x, int y)
14701 {
14702   if (!PLAYER_ENEMY_PROTECTED(x, y))
14703     KillPlayer(PLAYERINFO(x, y));
14704 }
14705
14706 static void KillPlayerUnlessExplosionProtected(int x, int y)
14707 {
14708   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14709     KillPlayer(PLAYERINFO(x, y));
14710 }
14711
14712 void BuryPlayer(struct PlayerInfo *player)
14713 {
14714   int jx = player->jx, jy = player->jy;
14715
14716   if (!player->active)
14717     return;
14718
14719   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14720   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14721
14722   player->GameOver = TRUE;
14723   RemovePlayer(player);
14724 }
14725
14726 void RemovePlayer(struct PlayerInfo *player)
14727 {
14728   int jx = player->jx, jy = player->jy;
14729   int i, found = FALSE;
14730
14731   player->present = FALSE;
14732   player->active = FALSE;
14733
14734   if (!ExplodeField[jx][jy])
14735     StorePlayer[jx][jy] = 0;
14736
14737   if (player->is_moving)
14738     TEST_DrawLevelField(player->last_jx, player->last_jy);
14739
14740   for (i = 0; i < MAX_PLAYERS; i++)
14741     if (stored_player[i].active)
14742       found = TRUE;
14743
14744   if (!found)
14745     AllPlayersGone = TRUE;
14746
14747   ExitX = ZX = jx;
14748   ExitY = ZY = jy;
14749 }
14750
14751 #if USE_NEW_SNAP_DELAY
14752 static void setFieldForSnapping(int x, int y, int element, int direction)
14753 {
14754   struct ElementInfo *ei = &element_info[element];
14755   int direction_bit = MV_DIR_TO_BIT(direction);
14756   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14757   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14758                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14759
14760   Feld[x][y] = EL_ELEMENT_SNAPPING;
14761   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14762
14763   ResetGfxAnimation(x, y);
14764
14765   GfxElement[x][y] = element;
14766   GfxAction[x][y] = action;
14767   GfxDir[x][y] = direction;
14768   GfxFrame[x][y] = -1;
14769 }
14770 #endif
14771
14772 /*
14773   =============================================================================
14774   checkDiagonalPushing()
14775   -----------------------------------------------------------------------------
14776   check if diagonal input device direction results in pushing of object
14777   (by checking if the alternative direction is walkable, diggable, ...)
14778   =============================================================================
14779 */
14780
14781 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14782                                     int x, int y, int real_dx, int real_dy)
14783 {
14784   int jx, jy, dx, dy, xx, yy;
14785
14786   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
14787     return TRUE;
14788
14789   /* diagonal direction: check alternative direction */
14790   jx = player->jx;
14791   jy = player->jy;
14792   dx = x - jx;
14793   dy = y - jy;
14794   xx = jx + (dx == 0 ? real_dx : 0);
14795   yy = jy + (dy == 0 ? real_dy : 0);
14796
14797   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14798 }
14799
14800 /*
14801   =============================================================================
14802   DigField()
14803   -----------------------------------------------------------------------------
14804   x, y:                 field next to player (non-diagonal) to try to dig to
14805   real_dx, real_dy:     direction as read from input device (can be diagonal)
14806   =============================================================================
14807 */
14808
14809 static int DigField(struct PlayerInfo *player,
14810                     int oldx, int oldy, int x, int y,
14811                     int real_dx, int real_dy, int mode)
14812 {
14813   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14814   boolean player_was_pushing = player->is_pushing;
14815   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14816   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14817   int jx = oldx, jy = oldy;
14818   int dx = x - jx, dy = y - jy;
14819   int nextx = x + dx, nexty = y + dy;
14820   int move_direction = (dx == -1 ? MV_LEFT  :
14821                         dx == +1 ? MV_RIGHT :
14822                         dy == -1 ? MV_UP    :
14823                         dy == +1 ? MV_DOWN  : MV_NONE);
14824   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14825   int dig_side = MV_DIR_OPPOSITE(move_direction);
14826   int old_element = Feld[jx][jy];
14827 #if USE_FIXED_DONT_RUN_INTO
14828   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14829 #else
14830   int element;
14831 #endif
14832   int collect_count;
14833
14834   if (is_player)                /* function can also be called by EL_PENGUIN */
14835   {
14836     if (player->MovPos == 0)
14837     {
14838       player->is_digging = FALSE;
14839       player->is_collecting = FALSE;
14840     }
14841
14842     if (player->MovPos == 0)    /* last pushing move finished */
14843       player->is_pushing = FALSE;
14844
14845     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
14846     {
14847       player->is_switching = FALSE;
14848       player->push_delay = -1;
14849
14850       return MP_NO_ACTION;
14851     }
14852   }
14853
14854 #if !USE_FIXED_DONT_RUN_INTO
14855   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14856     return MP_NO_ACTION;
14857 #endif
14858
14859   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14860     old_element = Back[jx][jy];
14861
14862   /* in case of element dropped at player position, check background */
14863   else if (Back[jx][jy] != EL_EMPTY &&
14864            game.engine_version >= VERSION_IDENT(2,2,0,0))
14865     old_element = Back[jx][jy];
14866
14867   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14868     return MP_NO_ACTION;        /* field has no opening in this direction */
14869
14870   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14871     return MP_NO_ACTION;        /* field has no opening in this direction */
14872
14873 #if USE_FIXED_DONT_RUN_INTO
14874   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14875   {
14876     SplashAcid(x, y);
14877
14878     Feld[jx][jy] = player->artwork_element;
14879     InitMovingField(jx, jy, MV_DOWN);
14880     Store[jx][jy] = EL_ACID;
14881     ContinueMoving(jx, jy);
14882     BuryPlayer(player);
14883
14884     return MP_DONT_RUN_INTO;
14885   }
14886 #endif
14887
14888 #if USE_FIXED_DONT_RUN_INTO
14889   if (player_can_move && DONT_RUN_INTO(element))
14890   {
14891     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14892
14893     return MP_DONT_RUN_INTO;
14894   }
14895 #endif
14896
14897 #if USE_FIXED_DONT_RUN_INTO
14898   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14899     return MP_NO_ACTION;
14900 #endif
14901
14902 #if !USE_FIXED_DONT_RUN_INTO
14903   element = Feld[x][y];
14904 #endif
14905
14906   collect_count = element_info[element].collect_count_initial;
14907
14908   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
14909     return MP_NO_ACTION;
14910
14911   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14912     player_can_move = player_can_move_or_snap;
14913
14914   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14915       game.engine_version >= VERSION_IDENT(2,2,0,0))
14916   {
14917     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14918                                player->index_bit, dig_side);
14919     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14920                                         player->index_bit, dig_side);
14921
14922     if (element == EL_DC_LANDMINE)
14923       Bang(x, y);
14924
14925     if (Feld[x][y] != element)          /* field changed by snapping */
14926       return MP_ACTION;
14927
14928     return MP_NO_ACTION;
14929   }
14930
14931 #if USE_PLAYER_GRAVITY
14932   if (player->gravity && is_player && !player->is_auto_moving &&
14933       canFallDown(player) && move_direction != MV_DOWN &&
14934       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14935     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14936 #else
14937   if (game.gravity && is_player && !player->is_auto_moving &&
14938       canFallDown(player) && move_direction != MV_DOWN &&
14939       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14940     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
14941 #endif
14942
14943   if (player_can_move &&
14944       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14945   {
14946     int sound_element = SND_ELEMENT(element);
14947     int sound_action = ACTION_WALKING;
14948
14949     if (IS_RND_GATE(element))
14950     {
14951       if (!player->key[RND_GATE_NR(element)])
14952         return MP_NO_ACTION;
14953     }
14954     else if (IS_RND_GATE_GRAY(element))
14955     {
14956       if (!player->key[RND_GATE_GRAY_NR(element)])
14957         return MP_NO_ACTION;
14958     }
14959     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14960     {
14961       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14962         return MP_NO_ACTION;
14963     }
14964     else if (element == EL_EXIT_OPEN ||
14965              element == EL_EM_EXIT_OPEN ||
14966 #if 1
14967              element == EL_EM_EXIT_OPENING ||
14968 #endif
14969              element == EL_STEEL_EXIT_OPEN ||
14970              element == EL_EM_STEEL_EXIT_OPEN ||
14971 #if 1
14972              element == EL_EM_STEEL_EXIT_OPENING ||
14973 #endif
14974              element == EL_SP_EXIT_OPEN ||
14975              element == EL_SP_EXIT_OPENING)
14976     {
14977       sound_action = ACTION_PASSING;    /* player is passing exit */
14978     }
14979     else if (element == EL_EMPTY)
14980     {
14981       sound_action = ACTION_MOVING;             /* nothing to walk on */
14982     }
14983
14984     /* play sound from background or player, whatever is available */
14985     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14986       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14987     else
14988       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14989   }
14990   else if (player_can_move &&
14991            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14992   {
14993     if (!ACCESS_FROM(element, opposite_direction))
14994       return MP_NO_ACTION;      /* field not accessible from this direction */
14995
14996     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14997       return MP_NO_ACTION;
14998
14999     if (IS_EM_GATE(element))
15000     {
15001       if (!player->key[EM_GATE_NR(element)])
15002         return MP_NO_ACTION;
15003     }
15004     else if (IS_EM_GATE_GRAY(element))
15005     {
15006       if (!player->key[EM_GATE_GRAY_NR(element)])
15007         return MP_NO_ACTION;
15008     }
15009     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15010     {
15011       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15012         return MP_NO_ACTION;
15013     }
15014     else if (IS_EMC_GATE(element))
15015     {
15016       if (!player->key[EMC_GATE_NR(element)])
15017         return MP_NO_ACTION;
15018     }
15019     else if (IS_EMC_GATE_GRAY(element))
15020     {
15021       if (!player->key[EMC_GATE_GRAY_NR(element)])
15022         return MP_NO_ACTION;
15023     }
15024     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15025     {
15026       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15027         return MP_NO_ACTION;
15028     }
15029     else if (element == EL_DC_GATE_WHITE ||
15030              element == EL_DC_GATE_WHITE_GRAY ||
15031              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15032     {
15033       if (player->num_white_keys == 0)
15034         return MP_NO_ACTION;
15035
15036       player->num_white_keys--;
15037     }
15038     else if (IS_SP_PORT(element))
15039     {
15040       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15041           element == EL_SP_GRAVITY_PORT_RIGHT ||
15042           element == EL_SP_GRAVITY_PORT_UP ||
15043           element == EL_SP_GRAVITY_PORT_DOWN)
15044 #if USE_PLAYER_GRAVITY
15045         player->gravity = !player->gravity;
15046 #else
15047         game.gravity = !game.gravity;
15048 #endif
15049       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15050                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15051                element == EL_SP_GRAVITY_ON_PORT_UP ||
15052                element == EL_SP_GRAVITY_ON_PORT_DOWN)
15053 #if USE_PLAYER_GRAVITY
15054         player->gravity = TRUE;
15055 #else
15056         game.gravity = TRUE;
15057 #endif
15058       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15059                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15060                element == EL_SP_GRAVITY_OFF_PORT_UP ||
15061                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15062 #if USE_PLAYER_GRAVITY
15063         player->gravity = FALSE;
15064 #else
15065         game.gravity = FALSE;
15066 #endif
15067     }
15068
15069     /* automatically move to the next field with double speed */
15070     player->programmed_action = move_direction;
15071
15072     if (player->move_delay_reset_counter == 0)
15073     {
15074       player->move_delay_reset_counter = 2;     /* two double speed steps */
15075
15076       DOUBLE_PLAYER_SPEED(player);
15077     }
15078
15079     PlayLevelSoundAction(x, y, ACTION_PASSING);
15080   }
15081   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15082   {
15083     RemoveField(x, y);
15084
15085     if (mode != DF_SNAP)
15086     {
15087       GfxElement[x][y] = GFX_ELEMENT(element);
15088       player->is_digging = TRUE;
15089     }
15090
15091     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15092
15093     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15094                                         player->index_bit, dig_side);
15095
15096     if (mode == DF_SNAP)
15097     {
15098 #if USE_NEW_SNAP_DELAY
15099       if (level.block_snap_field)
15100         setFieldForSnapping(x, y, element, move_direction);
15101       else
15102         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15103 #else
15104       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15105 #endif
15106
15107       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15108                                           player->index_bit, dig_side);
15109     }
15110   }
15111   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15112   {
15113     RemoveField(x, y);
15114
15115     if (is_player && mode != DF_SNAP)
15116     {
15117       GfxElement[x][y] = element;
15118       player->is_collecting = TRUE;
15119     }
15120
15121     if (element == EL_SPEED_PILL)
15122     {
15123       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15124     }
15125     else if (element == EL_EXTRA_TIME && level.time > 0)
15126     {
15127       TimeLeft += level.extra_time;
15128
15129 #if 1
15130       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15131
15132       DisplayGameControlValues();
15133 #else
15134       DrawGameValue_Time(TimeLeft);
15135 #endif
15136     }
15137     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15138     {
15139       player->shield_normal_time_left += level.shield_normal_time;
15140       if (element == EL_SHIELD_DEADLY)
15141         player->shield_deadly_time_left += level.shield_deadly_time;
15142     }
15143     else if (element == EL_DYNAMITE ||
15144              element == EL_EM_DYNAMITE ||
15145              element == EL_SP_DISK_RED)
15146     {
15147       if (player->inventory_size < MAX_INVENTORY_SIZE)
15148         player->inventory_element[player->inventory_size++] = element;
15149
15150       DrawGameDoorValues();
15151     }
15152     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15153     {
15154       player->dynabomb_count++;
15155       player->dynabombs_left++;
15156     }
15157     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15158     {
15159       player->dynabomb_size++;
15160     }
15161     else if (element == EL_DYNABOMB_INCREASE_POWER)
15162     {
15163       player->dynabomb_xl = TRUE;
15164     }
15165     else if (IS_KEY(element))
15166     {
15167       player->key[KEY_NR(element)] = TRUE;
15168
15169       DrawGameDoorValues();
15170     }
15171     else if (element == EL_DC_KEY_WHITE)
15172     {
15173       player->num_white_keys++;
15174
15175       /* display white keys? */
15176       /* DrawGameDoorValues(); */
15177     }
15178     else if (IS_ENVELOPE(element))
15179     {
15180       player->show_envelope = element;
15181     }
15182     else if (element == EL_EMC_LENSES)
15183     {
15184       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15185
15186       RedrawAllInvisibleElementsForLenses();
15187     }
15188     else if (element == EL_EMC_MAGNIFIER)
15189     {
15190       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15191
15192       RedrawAllInvisibleElementsForMagnifier();
15193     }
15194     else if (IS_DROPPABLE(element) ||
15195              IS_THROWABLE(element))     /* can be collected and dropped */
15196     {
15197       int i;
15198
15199       if (collect_count == 0)
15200         player->inventory_infinite_element = element;
15201       else
15202         for (i = 0; i < collect_count; i++)
15203           if (player->inventory_size < MAX_INVENTORY_SIZE)
15204             player->inventory_element[player->inventory_size++] = element;
15205
15206       DrawGameDoorValues();
15207     }
15208     else if (collect_count > 0)
15209     {
15210       local_player->gems_still_needed -= collect_count;
15211       if (local_player->gems_still_needed < 0)
15212         local_player->gems_still_needed = 0;
15213
15214 #if 1
15215       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15216
15217       DisplayGameControlValues();
15218 #else
15219       DrawGameValue_Emeralds(local_player->gems_still_needed);
15220 #endif
15221     }
15222
15223     RaiseScoreElement(element);
15224     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15225
15226     if (is_player)
15227       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15228                                           player->index_bit, dig_side);
15229
15230     if (mode == DF_SNAP)
15231     {
15232 #if USE_NEW_SNAP_DELAY
15233       if (level.block_snap_field)
15234         setFieldForSnapping(x, y, element, move_direction);
15235       else
15236         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
15237 #else
15238       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
15239 #endif
15240
15241       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15242                                           player->index_bit, dig_side);
15243     }
15244   }
15245   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15246   {
15247     if (mode == DF_SNAP && element != EL_BD_ROCK)
15248       return MP_NO_ACTION;
15249
15250     if (CAN_FALL(element) && dy)
15251       return MP_NO_ACTION;
15252
15253     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15254         !(element == EL_SPRING && level.use_spring_bug))
15255       return MP_NO_ACTION;
15256
15257     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15258         ((move_direction & MV_VERTICAL &&
15259           ((element_info[element].move_pattern & MV_LEFT &&
15260             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15261            (element_info[element].move_pattern & MV_RIGHT &&
15262             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15263          (move_direction & MV_HORIZONTAL &&
15264           ((element_info[element].move_pattern & MV_UP &&
15265             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15266            (element_info[element].move_pattern & MV_DOWN &&
15267             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15268       return MP_NO_ACTION;
15269
15270     /* do not push elements already moving away faster than player */
15271     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15272         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15273       return MP_NO_ACTION;
15274
15275     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15276     {
15277       if (player->push_delay_value == -1 || !player_was_pushing)
15278         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15279     }
15280     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15281     {
15282       if (player->push_delay_value == -1)
15283         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15284     }
15285     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15286     {
15287       if (!player->is_pushing)
15288         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15289     }
15290
15291     player->is_pushing = TRUE;
15292     player->is_active = TRUE;
15293
15294     if (!(IN_LEV_FIELD(nextx, nexty) &&
15295           (IS_FREE(nextx, nexty) ||
15296            (IS_SB_ELEMENT(element) &&
15297             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15298            (IS_CUSTOM_ELEMENT(element) &&
15299             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15300       return MP_NO_ACTION;
15301
15302     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15303       return MP_NO_ACTION;
15304
15305     if (player->push_delay == -1)       /* new pushing; restart delay */
15306       player->push_delay = 0;
15307
15308     if (player->push_delay < player->push_delay_value &&
15309         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15310         element != EL_SPRING && element != EL_BALLOON)
15311     {
15312       /* make sure that there is no move delay before next try to push */
15313       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15314         player->move_delay = 0;
15315
15316       return MP_NO_ACTION;
15317     }
15318
15319     if (IS_CUSTOM_ELEMENT(element) &&
15320         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15321     {
15322       if (!DigFieldByCE(nextx, nexty, element))
15323         return MP_NO_ACTION;
15324     }
15325
15326     if (IS_SB_ELEMENT(element))
15327     {
15328       if (element == EL_SOKOBAN_FIELD_FULL)
15329       {
15330         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15331         local_player->sokobanfields_still_needed++;
15332       }
15333
15334       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15335       {
15336         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15337         local_player->sokobanfields_still_needed--;
15338       }
15339
15340       Feld[x][y] = EL_SOKOBAN_OBJECT;
15341
15342       if (Back[x][y] == Back[nextx][nexty])
15343         PlayLevelSoundAction(x, y, ACTION_PUSHING);
15344       else if (Back[x][y] != 0)
15345         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15346                                     ACTION_EMPTYING);
15347       else
15348         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15349                                     ACTION_FILLING);
15350
15351       if (local_player->sokobanfields_still_needed == 0 &&
15352           game.emulation == EMU_SOKOBAN)
15353       {
15354         PlayerWins(player);
15355
15356         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15357       }
15358     }
15359     else
15360       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15361
15362     InitMovingField(x, y, move_direction);
15363     GfxAction[x][y] = ACTION_PUSHING;
15364
15365     if (mode == DF_SNAP)
15366       ContinueMoving(x, y);
15367     else
15368       MovPos[x][y] = (dx != 0 ? dx : dy);
15369
15370     Pushed[x][y] = TRUE;
15371     Pushed[nextx][nexty] = TRUE;
15372
15373     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15374       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15375     else
15376       player->push_delay_value = -1;    /* get new value later */
15377
15378     /* check for element change _after_ element has been pushed */
15379     if (game.use_change_when_pushing_bug)
15380     {
15381       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15382                                  player->index_bit, dig_side);
15383       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15384                                           player->index_bit, dig_side);
15385     }
15386   }
15387   else if (IS_SWITCHABLE(element))
15388   {
15389     if (PLAYER_SWITCHING(player, x, y))
15390     {
15391       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15392                                           player->index_bit, dig_side);
15393
15394       return MP_ACTION;
15395     }
15396
15397     player->is_switching = TRUE;
15398     player->switch_x = x;
15399     player->switch_y = y;
15400
15401     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15402
15403     if (element == EL_ROBOT_WHEEL)
15404     {
15405       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15406       ZX = x;
15407       ZY = y;
15408
15409       game.robot_wheel_active = TRUE;
15410
15411       TEST_DrawLevelField(x, y);
15412     }
15413     else if (element == EL_SP_TERMINAL)
15414     {
15415       int xx, yy;
15416
15417       SCAN_PLAYFIELD(xx, yy)
15418       {
15419         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15420           Bang(xx, yy);
15421         else if (Feld[xx][yy] == EL_SP_TERMINAL)
15422           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15423       }
15424     }
15425     else if (IS_BELT_SWITCH(element))
15426     {
15427       ToggleBeltSwitch(x, y);
15428     }
15429     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15430              element == EL_SWITCHGATE_SWITCH_DOWN ||
15431              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15432              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15433     {
15434       ToggleSwitchgateSwitch(x, y);
15435     }
15436     else if (element == EL_LIGHT_SWITCH ||
15437              element == EL_LIGHT_SWITCH_ACTIVE)
15438     {
15439       ToggleLightSwitch(x, y);
15440     }
15441     else if (element == EL_TIMEGATE_SWITCH ||
15442              element == EL_DC_TIMEGATE_SWITCH)
15443     {
15444       ActivateTimegateSwitch(x, y);
15445     }
15446     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15447              element == EL_BALLOON_SWITCH_RIGHT ||
15448              element == EL_BALLOON_SWITCH_UP    ||
15449              element == EL_BALLOON_SWITCH_DOWN  ||
15450              element == EL_BALLOON_SWITCH_NONE  ||
15451              element == EL_BALLOON_SWITCH_ANY)
15452     {
15453       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15454                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15455                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15456                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15457                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15458                              move_direction);
15459     }
15460     else if (element == EL_LAMP)
15461     {
15462       Feld[x][y] = EL_LAMP_ACTIVE;
15463       local_player->lights_still_needed--;
15464
15465       ResetGfxAnimation(x, y);
15466       TEST_DrawLevelField(x, y);
15467     }
15468     else if (element == EL_TIME_ORB_FULL)
15469     {
15470       Feld[x][y] = EL_TIME_ORB_EMPTY;
15471
15472       if (level.time > 0 || level.use_time_orb_bug)
15473       {
15474         TimeLeft += level.time_orb_time;
15475
15476 #if 1
15477         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15478
15479         DisplayGameControlValues();
15480 #else
15481         DrawGameValue_Time(TimeLeft);
15482 #endif
15483       }
15484
15485       ResetGfxAnimation(x, y);
15486       TEST_DrawLevelField(x, y);
15487     }
15488     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15489              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15490     {
15491       int xx, yy;
15492
15493       game.ball_state = !game.ball_state;
15494
15495       SCAN_PLAYFIELD(xx, yy)
15496       {
15497         int e = Feld[xx][yy];
15498
15499         if (game.ball_state)
15500         {
15501           if (e == EL_EMC_MAGIC_BALL)
15502             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15503           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15504             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15505         }
15506         else
15507         {
15508           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15509             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15510           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15511             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15512         }
15513       }
15514     }
15515
15516     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15517                                         player->index_bit, dig_side);
15518
15519     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15520                                         player->index_bit, dig_side);
15521
15522     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15523                                         player->index_bit, dig_side);
15524
15525     return MP_ACTION;
15526   }
15527   else
15528   {
15529     if (!PLAYER_SWITCHING(player, x, y))
15530     {
15531       player->is_switching = TRUE;
15532       player->switch_x = x;
15533       player->switch_y = y;
15534
15535       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15536                                  player->index_bit, dig_side);
15537       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15538                                           player->index_bit, dig_side);
15539
15540       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15541                                  player->index_bit, dig_side);
15542       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15543                                           player->index_bit, dig_side);
15544     }
15545
15546     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15547                                player->index_bit, dig_side);
15548     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15549                                         player->index_bit, dig_side);
15550
15551     return MP_NO_ACTION;
15552   }
15553
15554   player->push_delay = -1;
15555
15556   if (is_player)                /* function can also be called by EL_PENGUIN */
15557   {
15558     if (Feld[x][y] != element)          /* really digged/collected something */
15559     {
15560       player->is_collecting = !player->is_digging;
15561       player->is_active = TRUE;
15562     }
15563   }
15564
15565   return MP_MOVING;
15566 }
15567
15568 static boolean DigFieldByCE(int x, int y, int digging_element)
15569 {
15570   int element = Feld[x][y];
15571
15572   if (!IS_FREE(x, y))
15573   {
15574     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15575                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15576                   ACTION_BREAKING);
15577
15578     /* no element can dig solid indestructible elements */
15579     if (IS_INDESTRUCTIBLE(element) &&
15580         !IS_DIGGABLE(element) &&
15581         !IS_COLLECTIBLE(element))
15582       return FALSE;
15583
15584     if (AmoebaNr[x][y] &&
15585         (element == EL_AMOEBA_FULL ||
15586          element == EL_BD_AMOEBA ||
15587          element == EL_AMOEBA_GROWING))
15588     {
15589       AmoebaCnt[AmoebaNr[x][y]]--;
15590       AmoebaCnt2[AmoebaNr[x][y]]--;
15591     }
15592
15593     if (IS_MOVING(x, y))
15594       RemoveMovingField(x, y);
15595     else
15596     {
15597       RemoveField(x, y);
15598       TEST_DrawLevelField(x, y);
15599     }
15600
15601     /* if digged element was about to explode, prevent the explosion */
15602     ExplodeField[x][y] = EX_TYPE_NONE;
15603
15604     PlayLevelSoundAction(x, y, action);
15605   }
15606
15607   Store[x][y] = EL_EMPTY;
15608
15609 #if 1
15610   /* this makes it possible to leave the removed element again */
15611   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15612     Store[x][y] = element;
15613 #else
15614   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15615   {
15616     int move_leave_element = element_info[digging_element].move_leave_element;
15617
15618     /* this makes it possible to leave the removed element again */
15619     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15620                    element : move_leave_element);
15621   }
15622 #endif
15623
15624   return TRUE;
15625 }
15626
15627 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15628 {
15629   int jx = player->jx, jy = player->jy;
15630   int x = jx + dx, y = jy + dy;
15631   int snap_direction = (dx == -1 ? MV_LEFT  :
15632                         dx == +1 ? MV_RIGHT :
15633                         dy == -1 ? MV_UP    :
15634                         dy == +1 ? MV_DOWN  : MV_NONE);
15635   boolean can_continue_snapping = (level.continuous_snapping &&
15636                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15637
15638   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15639     return FALSE;
15640
15641   if (!player->active || !IN_LEV_FIELD(x, y))
15642     return FALSE;
15643
15644   if (dx && dy)
15645     return FALSE;
15646
15647   if (!dx && !dy)
15648   {
15649     if (player->MovPos == 0)
15650       player->is_pushing = FALSE;
15651
15652     player->is_snapping = FALSE;
15653
15654     if (player->MovPos == 0)
15655     {
15656       player->is_moving = FALSE;
15657       player->is_digging = FALSE;
15658       player->is_collecting = FALSE;
15659     }
15660
15661     return FALSE;
15662   }
15663
15664 #if USE_NEW_CONTINUOUS_SNAPPING
15665   /* prevent snapping with already pressed snap key when not allowed */
15666   if (player->is_snapping && !can_continue_snapping)
15667     return FALSE;
15668 #else
15669   if (player->is_snapping)
15670     return FALSE;
15671 #endif
15672
15673   player->MovDir = snap_direction;
15674
15675   if (player->MovPos == 0)
15676   {
15677     player->is_moving = FALSE;
15678     player->is_digging = FALSE;
15679     player->is_collecting = FALSE;
15680   }
15681
15682   player->is_dropping = FALSE;
15683   player->is_dropping_pressed = FALSE;
15684   player->drop_pressed_delay = 0;
15685
15686   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15687     return FALSE;
15688
15689   player->is_snapping = TRUE;
15690   player->is_active = TRUE;
15691
15692   if (player->MovPos == 0)
15693   {
15694     player->is_moving = FALSE;
15695     player->is_digging = FALSE;
15696     player->is_collecting = FALSE;
15697   }
15698
15699   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
15700     TEST_DrawLevelField(player->last_jx, player->last_jy);
15701
15702   TEST_DrawLevelField(x, y);
15703
15704   return TRUE;
15705 }
15706
15707 static boolean DropElement(struct PlayerInfo *player)
15708 {
15709   int old_element, new_element;
15710   int dropx = player->jx, dropy = player->jy;
15711   int drop_direction = player->MovDir;
15712   int drop_side = drop_direction;
15713 #if 1
15714   int drop_element = get_next_dropped_element(player);
15715 #else
15716   int drop_element = (player->inventory_size > 0 ?
15717                       player->inventory_element[player->inventory_size - 1] :
15718                       player->inventory_infinite_element != EL_UNDEFINED ?
15719                       player->inventory_infinite_element :
15720                       player->dynabombs_left > 0 ?
15721                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15722                       EL_UNDEFINED);
15723 #endif
15724
15725   player->is_dropping_pressed = TRUE;
15726
15727   /* do not drop an element on top of another element; when holding drop key
15728      pressed without moving, dropped element must move away before the next
15729      element can be dropped (this is especially important if the next element
15730      is dynamite, which can be placed on background for historical reasons) */
15731   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15732     return MP_ACTION;
15733
15734   if (IS_THROWABLE(drop_element))
15735   {
15736     dropx += GET_DX_FROM_DIR(drop_direction);
15737     dropy += GET_DY_FROM_DIR(drop_direction);
15738
15739     if (!IN_LEV_FIELD(dropx, dropy))
15740       return FALSE;
15741   }
15742
15743   old_element = Feld[dropx][dropy];     /* old element at dropping position */
15744   new_element = drop_element;           /* default: no change when dropping */
15745
15746   /* check if player is active, not moving and ready to drop */
15747   if (!player->active || player->MovPos || player->drop_delay > 0)
15748     return FALSE;
15749
15750   /* check if player has anything that can be dropped */
15751   if (new_element == EL_UNDEFINED)
15752     return FALSE;
15753
15754   /* check if drop key was pressed long enough for EM style dynamite */
15755   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15756     return FALSE;
15757
15758   /* check if anything can be dropped at the current position */
15759   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15760     return FALSE;
15761
15762   /* collected custom elements can only be dropped on empty fields */
15763   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15764     return FALSE;
15765
15766   if (old_element != EL_EMPTY)
15767     Back[dropx][dropy] = old_element;   /* store old element on this field */
15768
15769   ResetGfxAnimation(dropx, dropy);
15770   ResetRandomAnimationValue(dropx, dropy);
15771
15772   if (player->inventory_size > 0 ||
15773       player->inventory_infinite_element != EL_UNDEFINED)
15774   {
15775     if (player->inventory_size > 0)
15776     {
15777       player->inventory_size--;
15778
15779       DrawGameDoorValues();
15780
15781       if (new_element == EL_DYNAMITE)
15782         new_element = EL_DYNAMITE_ACTIVE;
15783       else if (new_element == EL_EM_DYNAMITE)
15784         new_element = EL_EM_DYNAMITE_ACTIVE;
15785       else if (new_element == EL_SP_DISK_RED)
15786         new_element = EL_SP_DISK_RED_ACTIVE;
15787     }
15788
15789     Feld[dropx][dropy] = new_element;
15790
15791     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15792       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15793                           el2img(Feld[dropx][dropy]), 0);
15794
15795     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15796
15797     /* needed if previous element just changed to "empty" in the last frame */
15798     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15799
15800     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15801                                player->index_bit, drop_side);
15802     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15803                                         CE_PLAYER_DROPS_X,
15804                                         player->index_bit, drop_side);
15805
15806     TestIfElementTouchesCustomElement(dropx, dropy);
15807   }
15808   else          /* player is dropping a dyna bomb */
15809   {
15810     player->dynabombs_left--;
15811
15812     Feld[dropx][dropy] = new_element;
15813
15814     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15815       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15816                           el2img(Feld[dropx][dropy]), 0);
15817
15818     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15819   }
15820
15821   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15822     InitField_WithBug1(dropx, dropy, FALSE);
15823
15824   new_element = Feld[dropx][dropy];     /* element might have changed */
15825
15826   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15827       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15828   {
15829     int move_direction, nextx, nexty;
15830
15831     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15832       MovDir[dropx][dropy] = drop_direction;
15833
15834     move_direction = MovDir[dropx][dropy];
15835     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15836     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15837
15838     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
15839
15840 #if USE_FIX_IMPACT_COLLISION
15841     /* do not cause impact style collision by dropping elements that can fall */
15842     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15843 #else
15844     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15845 #endif
15846   }
15847
15848   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15849   player->is_dropping = TRUE;
15850
15851   player->drop_pressed_delay = 0;
15852   player->is_dropping_pressed = FALSE;
15853
15854   player->drop_x = dropx;
15855   player->drop_y = dropy;
15856
15857   return TRUE;
15858 }
15859
15860 /* ------------------------------------------------------------------------- */
15861 /* game sound playing functions                                              */
15862 /* ------------------------------------------------------------------------- */
15863
15864 static int *loop_sound_frame = NULL;
15865 static int *loop_sound_volume = NULL;
15866
15867 void InitPlayLevelSound()
15868 {
15869   int num_sounds = getSoundListSize();
15870
15871   checked_free(loop_sound_frame);
15872   checked_free(loop_sound_volume);
15873
15874   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15875   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15876 }
15877
15878 static void PlayLevelSound(int x, int y, int nr)
15879 {
15880   int sx = SCREENX(x), sy = SCREENY(y);
15881   int volume, stereo_position;
15882   int max_distance = 8;
15883   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15884
15885   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15886       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15887     return;
15888
15889   if (!IN_LEV_FIELD(x, y) ||
15890       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15891       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15892     return;
15893
15894   volume = SOUND_MAX_VOLUME;
15895
15896   if (!IN_SCR_FIELD(sx, sy))
15897   {
15898     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15899     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15900
15901     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15902   }
15903
15904   stereo_position = (SOUND_MAX_LEFT +
15905                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15906                      (SCR_FIELDX + 2 * max_distance));
15907
15908   if (IS_LOOP_SOUND(nr))
15909   {
15910     /* This assures that quieter loop sounds do not overwrite louder ones,
15911        while restarting sound volume comparison with each new game frame. */
15912
15913     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15914       return;
15915
15916     loop_sound_volume[nr] = volume;
15917     loop_sound_frame[nr] = FrameCounter;
15918   }
15919
15920   PlaySoundExt(nr, volume, stereo_position, type);
15921 }
15922
15923 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15924 {
15925   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15926                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15927                  y < LEVELY(BY1) ? LEVELY(BY1) :
15928                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15929                  sound_action);
15930 }
15931
15932 static void PlayLevelSoundAction(int x, int y, int action)
15933 {
15934   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15935 }
15936
15937 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15938 {
15939   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15940
15941   if (sound_effect != SND_UNDEFINED)
15942     PlayLevelSound(x, y, sound_effect);
15943 }
15944
15945 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15946                                               int action)
15947 {
15948   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15949
15950   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15951     PlayLevelSound(x, y, sound_effect);
15952 }
15953
15954 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15955 {
15956   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15957
15958   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15959     PlayLevelSound(x, y, sound_effect);
15960 }
15961
15962 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15963 {
15964   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15965
15966   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15967     StopSound(sound_effect);
15968 }
15969
15970 static void PlayLevelMusic()
15971 {
15972   if (levelset.music[level_nr] != MUS_UNDEFINED)
15973     PlayMusic(levelset.music[level_nr]);        /* from config file */
15974   else
15975     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
15976 }
15977
15978 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15979 {
15980   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15981   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15982   int x = xx - 1 - offset;
15983   int y = yy - 1 - offset;
15984
15985   switch (sample)
15986   {
15987     case SAMPLE_blank:
15988       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15989       break;
15990
15991     case SAMPLE_roll:
15992       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15993       break;
15994
15995     case SAMPLE_stone:
15996       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15997       break;
15998
15999     case SAMPLE_nut:
16000       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16001       break;
16002
16003     case SAMPLE_crack:
16004       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16005       break;
16006
16007     case SAMPLE_bug:
16008       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16009       break;
16010
16011     case SAMPLE_tank:
16012       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16013       break;
16014
16015     case SAMPLE_android_clone:
16016       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16017       break;
16018
16019     case SAMPLE_android_move:
16020       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16021       break;
16022
16023     case SAMPLE_spring:
16024       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16025       break;
16026
16027     case SAMPLE_slurp:
16028       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16029       break;
16030
16031     case SAMPLE_eater:
16032       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16033       break;
16034
16035     case SAMPLE_eater_eat:
16036       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16037       break;
16038
16039     case SAMPLE_alien:
16040       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16041       break;
16042
16043     case SAMPLE_collect:
16044       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16045       break;
16046
16047     case SAMPLE_diamond:
16048       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16049       break;
16050
16051     case SAMPLE_squash:
16052       /* !!! CHECK THIS !!! */
16053 #if 1
16054       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16055 #else
16056       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16057 #endif
16058       break;
16059
16060     case SAMPLE_wonderfall:
16061       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16062       break;
16063
16064     case SAMPLE_drip:
16065       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16066       break;
16067
16068     case SAMPLE_push:
16069       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16070       break;
16071
16072     case SAMPLE_dirt:
16073       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16074       break;
16075
16076     case SAMPLE_acid:
16077       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16078       break;
16079
16080     case SAMPLE_ball:
16081       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16082       break;
16083
16084     case SAMPLE_grow:
16085       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16086       break;
16087
16088     case SAMPLE_wonder:
16089       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16090       break;
16091
16092     case SAMPLE_door:
16093       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16094       break;
16095
16096     case SAMPLE_exit_open:
16097       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16098       break;
16099
16100     case SAMPLE_exit_leave:
16101       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16102       break;
16103
16104     case SAMPLE_dynamite:
16105       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16106       break;
16107
16108     case SAMPLE_tick:
16109       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16110       break;
16111
16112     case SAMPLE_press:
16113       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16114       break;
16115
16116     case SAMPLE_wheel:
16117       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16118       break;
16119
16120     case SAMPLE_boom:
16121       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16122       break;
16123
16124     case SAMPLE_die:
16125       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16126       break;
16127
16128     case SAMPLE_time:
16129       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16130       break;
16131
16132     default:
16133       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16134       break;
16135   }
16136 }
16137
16138 #if 0
16139 void ChangeTime(int value)
16140 {
16141   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16142
16143   *time += value;
16144
16145   /* EMC game engine uses value from time counter of RND game engine */
16146   level.native_em_level->lev->time = *time;
16147
16148   DrawGameValue_Time(*time);
16149 }
16150
16151 void RaiseScore(int value)
16152 {
16153   /* EMC game engine and RND game engine have separate score counters */
16154   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16155                 &level.native_em_level->lev->score : &local_player->score);
16156
16157   *score += value;
16158
16159   DrawGameValue_Score(*score);
16160 }
16161 #endif
16162
16163 void RaiseScore(int value)
16164 {
16165   local_player->score += value;
16166
16167 #if 1
16168   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16169
16170   DisplayGameControlValues();
16171 #else
16172   DrawGameValue_Score(local_player->score);
16173 #endif
16174 }
16175
16176 void RaiseScoreElement(int element)
16177 {
16178   switch (element)
16179   {
16180     case EL_EMERALD:
16181     case EL_BD_DIAMOND:
16182     case EL_EMERALD_YELLOW:
16183     case EL_EMERALD_RED:
16184     case EL_EMERALD_PURPLE:
16185     case EL_SP_INFOTRON:
16186       RaiseScore(level.score[SC_EMERALD]);
16187       break;
16188     case EL_DIAMOND:
16189       RaiseScore(level.score[SC_DIAMOND]);
16190       break;
16191     case EL_CRYSTAL:
16192       RaiseScore(level.score[SC_CRYSTAL]);
16193       break;
16194     case EL_PEARL:
16195       RaiseScore(level.score[SC_PEARL]);
16196       break;
16197     case EL_BUG:
16198     case EL_BD_BUTTERFLY:
16199     case EL_SP_ELECTRON:
16200       RaiseScore(level.score[SC_BUG]);
16201       break;
16202     case EL_SPACESHIP:
16203     case EL_BD_FIREFLY:
16204     case EL_SP_SNIKSNAK:
16205       RaiseScore(level.score[SC_SPACESHIP]);
16206       break;
16207     case EL_YAMYAM:
16208     case EL_DARK_YAMYAM:
16209       RaiseScore(level.score[SC_YAMYAM]);
16210       break;
16211     case EL_ROBOT:
16212       RaiseScore(level.score[SC_ROBOT]);
16213       break;
16214     case EL_PACMAN:
16215       RaiseScore(level.score[SC_PACMAN]);
16216       break;
16217     case EL_NUT:
16218       RaiseScore(level.score[SC_NUT]);
16219       break;
16220     case EL_DYNAMITE:
16221     case EL_EM_DYNAMITE:
16222     case EL_SP_DISK_RED:
16223     case EL_DYNABOMB_INCREASE_NUMBER:
16224     case EL_DYNABOMB_INCREASE_SIZE:
16225     case EL_DYNABOMB_INCREASE_POWER:
16226       RaiseScore(level.score[SC_DYNAMITE]);
16227       break;
16228     case EL_SHIELD_NORMAL:
16229     case EL_SHIELD_DEADLY:
16230       RaiseScore(level.score[SC_SHIELD]);
16231       break;
16232     case EL_EXTRA_TIME:
16233       RaiseScore(level.extra_time_score);
16234       break;
16235     case EL_KEY_1:
16236     case EL_KEY_2:
16237     case EL_KEY_3:
16238     case EL_KEY_4:
16239     case EL_EM_KEY_1:
16240     case EL_EM_KEY_2:
16241     case EL_EM_KEY_3:
16242     case EL_EM_KEY_4:
16243     case EL_EMC_KEY_5:
16244     case EL_EMC_KEY_6:
16245     case EL_EMC_KEY_7:
16246     case EL_EMC_KEY_8:
16247     case EL_DC_KEY_WHITE:
16248       RaiseScore(level.score[SC_KEY]);
16249       break;
16250     default:
16251       RaiseScore(element_info[element].collect_score);
16252       break;
16253   }
16254 }
16255
16256 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16257 {
16258   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16259   {
16260 #if defined(NETWORK_AVALIABLE)
16261     if (options.network)
16262       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16263     else
16264 #endif
16265     {
16266       if (quick_quit)
16267       {
16268 #if 1
16269
16270 #if 1
16271         FadeSkipNextFadeIn();
16272 #else
16273         fading = fading_none;
16274 #endif
16275
16276 #else
16277         OpenDoor(DOOR_CLOSE_1);
16278 #endif
16279
16280         game_status = GAME_MODE_MAIN;
16281
16282 #if 1
16283         DrawAndFadeInMainMenu(REDRAW_FIELD);
16284 #else
16285         DrawMainMenu();
16286 #endif
16287       }
16288       else
16289       {
16290 #if 0
16291         FadeOut(REDRAW_FIELD);
16292 #endif
16293
16294         game_status = GAME_MODE_MAIN;
16295
16296         DrawAndFadeInMainMenu(REDRAW_FIELD);
16297       }
16298     }
16299   }
16300   else          /* continue playing the game */
16301   {
16302     if (tape.playing && tape.deactivate_display)
16303       TapeDeactivateDisplayOff(TRUE);
16304
16305     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16306
16307     if (tape.playing && tape.deactivate_display)
16308       TapeDeactivateDisplayOn();
16309   }
16310 }
16311
16312 void RequestQuitGame(boolean ask_if_really_quit)
16313 {
16314   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16315   boolean skip_request = AllPlayersGone || quick_quit;
16316
16317   RequestQuitGameExt(skip_request, quick_quit,
16318                      "Do you really want to quit the game ?");
16319 }
16320
16321
16322 /* ------------------------------------------------------------------------- */
16323 /* random generator functions                                                */
16324 /* ------------------------------------------------------------------------- */
16325
16326 unsigned int InitEngineRandom_RND(long seed)
16327 {
16328   game.num_random_calls = 0;
16329
16330 #if 0
16331   unsigned int rnd_seed = InitEngineRandom(seed);
16332
16333   printf("::: START RND: %d\n", rnd_seed);
16334
16335   return rnd_seed;
16336 #else
16337
16338   return InitEngineRandom(seed);
16339
16340 #endif
16341
16342 }
16343
16344 unsigned int RND(int max)
16345 {
16346   if (max > 0)
16347   {
16348     game.num_random_calls++;
16349
16350     return GetEngineRandom(max);
16351   }
16352
16353   return 0;
16354 }
16355
16356
16357 /* ------------------------------------------------------------------------- */
16358 /* game engine snapshot handling functions                                   */
16359 /* ------------------------------------------------------------------------- */
16360
16361 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
16362
16363 struct EngineSnapshotInfo
16364 {
16365   /* runtime values for custom element collect score */
16366   int collect_score[NUM_CUSTOM_ELEMENTS];
16367
16368   /* runtime values for group element choice position */
16369   int choice_pos[NUM_GROUP_ELEMENTS];
16370
16371   /* runtime values for belt position animations */
16372   int belt_graphic[4 * NUM_BELT_PARTS];
16373   int belt_anim_mode[4 * NUM_BELT_PARTS];
16374 };
16375
16376 struct EngineSnapshotNodeInfo
16377 {
16378   void *buffer_orig;
16379   void *buffer_copy;
16380   int size;
16381 };
16382
16383 static struct EngineSnapshotInfo engine_snapshot_rnd;
16384 static ListNode *engine_snapshot_list = NULL;
16385 static char *snapshot_level_identifier = NULL;
16386 static int snapshot_level_nr = -1;
16387
16388 void FreeEngineSnapshot()
16389 {
16390   while (engine_snapshot_list != NULL)
16391     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16392                        checked_free);
16393
16394   setString(&snapshot_level_identifier, NULL);
16395   snapshot_level_nr = -1;
16396 }
16397
16398 static void SaveEngineSnapshotValues_RND()
16399 {
16400   static int belt_base_active_element[4] =
16401   {
16402     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16403     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16404     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16405     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16406   };
16407   int i, j;
16408
16409   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16410   {
16411     int element = EL_CUSTOM_START + i;
16412
16413     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16414   }
16415
16416   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16417   {
16418     int element = EL_GROUP_START + i;
16419
16420     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16421   }
16422
16423   for (i = 0; i < 4; i++)
16424   {
16425     for (j = 0; j < NUM_BELT_PARTS; j++)
16426     {
16427       int element = belt_base_active_element[i] + j;
16428       int graphic = el2img(element);
16429       int anim_mode = graphic_info[graphic].anim_mode;
16430
16431       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16432       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16433     }
16434   }
16435 }
16436
16437 static void LoadEngineSnapshotValues_RND()
16438 {
16439   unsigned long num_random_calls = game.num_random_calls;
16440   int i, j;
16441
16442   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16443   {
16444     int element = EL_CUSTOM_START + i;
16445
16446     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16447   }
16448
16449   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16450   {
16451     int element = EL_GROUP_START + i;
16452
16453     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16454   }
16455
16456   for (i = 0; i < 4; i++)
16457   {
16458     for (j = 0; j < NUM_BELT_PARTS; j++)
16459     {
16460       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16461       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16462
16463       graphic_info[graphic].anim_mode = anim_mode;
16464     }
16465   }
16466
16467   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16468   {
16469     InitRND(tape.random_seed);
16470     for (i = 0; i < num_random_calls; i++)
16471       RND(1);
16472   }
16473
16474   if (game.num_random_calls != num_random_calls)
16475   {
16476     Error(ERR_INFO, "number of random calls out of sync");
16477     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16478     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16479     Error(ERR_EXIT, "this should not happen -- please debug");
16480   }
16481 }
16482
16483 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16484 {
16485   struct EngineSnapshotNodeInfo *bi =
16486     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16487
16488   bi->buffer_orig = buffer;
16489   bi->buffer_copy = checked_malloc(size);
16490   bi->size = size;
16491
16492   memcpy(bi->buffer_copy, buffer, size);
16493
16494   addNodeToList(&engine_snapshot_list, NULL, bi);
16495 }
16496
16497 void SaveEngineSnapshot()
16498 {
16499   FreeEngineSnapshot();         /* free previous snapshot, if needed */
16500
16501   if (level_editor_test_game)   /* do not save snapshots from editor */
16502     return;
16503
16504   /* copy some special values to a structure better suited for the snapshot */
16505
16506   SaveEngineSnapshotValues_RND();
16507   SaveEngineSnapshotValues_EM();
16508
16509   /* save values stored in special snapshot structure */
16510
16511   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16512   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16513
16514   /* save further RND engine values */
16515
16516   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16517   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16519
16520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16521   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16522   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16524
16525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16530
16531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16534
16535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16536
16537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16538
16539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16541
16542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16560
16561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16563
16564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16567
16568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16570
16571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16576
16577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16579
16580   /* save level identification information */
16581
16582   setString(&snapshot_level_identifier, leveldir_current->identifier);
16583   snapshot_level_nr = level_nr;
16584
16585 #if 0
16586   ListNode *node = engine_snapshot_list;
16587   int num_bytes = 0;
16588
16589   while (node != NULL)
16590   {
16591     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16592
16593     node = node->next;
16594   }
16595
16596   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16597 #endif
16598 }
16599
16600 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16601 {
16602   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16603 }
16604
16605 void LoadEngineSnapshot()
16606 {
16607   ListNode *node = engine_snapshot_list;
16608
16609   if (engine_snapshot_list == NULL)
16610     return;
16611
16612   while (node != NULL)
16613   {
16614     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16615
16616     node = node->next;
16617   }
16618
16619   /* restore special values from snapshot structure */
16620
16621   LoadEngineSnapshotValues_RND();
16622   LoadEngineSnapshotValues_EM();
16623 }
16624
16625 boolean CheckEngineSnapshot()
16626 {
16627   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16628           snapshot_level_nr == level_nr);
16629 }
16630
16631
16632 /* ---------- new game button stuff ---------------------------------------- */
16633
16634 /* graphic position values for game buttons */
16635 #define GAME_BUTTON_XSIZE       30
16636 #define GAME_BUTTON_YSIZE       30
16637 #define GAME_BUTTON_XPOS        5
16638 #define GAME_BUTTON_YPOS        215
16639 #define SOUND_BUTTON_XPOS       5
16640 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16641
16642 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16643 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16644 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16645 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16646 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16647 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16648
16649 static struct
16650 {
16651   int *x, *y;
16652   int gd_x, gd_y;
16653   int gadget_id;
16654   char *infotext;
16655 } gamebutton_info[NUM_GAME_BUTTONS] =
16656 {
16657 #if 1
16658   {
16659     &game.button.stop.x,        &game.button.stop.y,
16660     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16661     GAME_CTRL_ID_STOP,
16662     "stop game"
16663   },
16664   {
16665     &game.button.pause.x,       &game.button.pause.y,
16666     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16667     GAME_CTRL_ID_PAUSE,
16668     "pause game"
16669   },
16670   {
16671     &game.button.play.x,        &game.button.play.y,
16672     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16673     GAME_CTRL_ID_PLAY,
16674     "play game"
16675   },
16676   {
16677     &game.button.sound_music.x, &game.button.sound_music.y,
16678     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16679     SOUND_CTRL_ID_MUSIC,
16680     "background music on/off"
16681   },
16682   {
16683     &game.button.sound_loops.x, &game.button.sound_loops.y,
16684     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16685     SOUND_CTRL_ID_LOOPS,
16686     "sound loops on/off"
16687   },
16688   {
16689     &game.button.sound_simple.x,&game.button.sound_simple.y,
16690     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16691     SOUND_CTRL_ID_SIMPLE,
16692     "normal sounds on/off"
16693   }
16694 #else
16695   {
16696     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
16697     GAME_CTRL_ID_STOP,
16698     "stop game"
16699   },
16700   {
16701     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
16702     GAME_CTRL_ID_PAUSE,
16703     "pause game"
16704   },
16705   {
16706     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
16707     GAME_CTRL_ID_PLAY,
16708     "play game"
16709   },
16710   {
16711     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
16712     SOUND_CTRL_ID_MUSIC,
16713     "background music on/off"
16714   },
16715   {
16716     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
16717     SOUND_CTRL_ID_LOOPS,
16718     "sound loops on/off"
16719   },
16720   {
16721     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
16722     SOUND_CTRL_ID_SIMPLE,
16723     "normal sounds on/off"
16724   }
16725 #endif
16726 };
16727
16728 void CreateGameButtons()
16729 {
16730   int i;
16731
16732   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16733   {
16734     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16735     struct GadgetInfo *gi;
16736     int button_type;
16737     boolean checked;
16738     unsigned long event_mask;
16739     int x, y;
16740     int gd_xoffset, gd_yoffset;
16741     int gd_x1, gd_x2, gd_y1, gd_y2;
16742     int id = i;
16743
16744     x = DX + *gamebutton_info[i].x;
16745     y = DY + *gamebutton_info[i].y;
16746     gd_xoffset = gamebutton_info[i].gd_x;
16747     gd_yoffset = gamebutton_info[i].gd_y;
16748     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16749     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16750
16751     if (id == GAME_CTRL_ID_STOP ||
16752         id == GAME_CTRL_ID_PAUSE ||
16753         id == GAME_CTRL_ID_PLAY)
16754     {
16755       button_type = GD_TYPE_NORMAL_BUTTON;
16756       checked = FALSE;
16757       event_mask = GD_EVENT_RELEASED;
16758       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16759       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16760     }
16761     else
16762     {
16763       button_type = GD_TYPE_CHECK_BUTTON;
16764       checked =
16765         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16766          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16767          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16768       event_mask = GD_EVENT_PRESSED;
16769       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16770       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16771     }
16772
16773     gi = CreateGadget(GDI_CUSTOM_ID, id,
16774                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16775 #if 1
16776                       GDI_X, x,
16777                       GDI_Y, y,
16778 #else
16779                       GDI_X, DX + gd_xoffset,
16780                       GDI_Y, DY + gd_yoffset,
16781 #endif
16782                       GDI_WIDTH, GAME_BUTTON_XSIZE,
16783                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
16784                       GDI_TYPE, button_type,
16785                       GDI_STATE, GD_BUTTON_UNPRESSED,
16786                       GDI_CHECKED, checked,
16787                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16788                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16789                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16790                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16791                       GDI_DIRECT_DRAW, FALSE,
16792                       GDI_EVENT_MASK, event_mask,
16793                       GDI_CALLBACK_ACTION, HandleGameButtons,
16794                       GDI_END);
16795
16796     if (gi == NULL)
16797       Error(ERR_EXIT, "cannot create gadget");
16798
16799     game_gadget[id] = gi;
16800   }
16801 }
16802
16803 void FreeGameButtons()
16804 {
16805   int i;
16806
16807   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16808     FreeGadget(game_gadget[i]);
16809 }
16810
16811 static void MapGameButtons()
16812 {
16813   int i;
16814
16815   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16816     MapGadget(game_gadget[i]);
16817 }
16818
16819 void UnmapGameButtons()
16820 {
16821   int i;
16822
16823   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16824     UnmapGadget(game_gadget[i]);
16825 }
16826
16827 void RedrawGameButtons()
16828 {
16829   int i;
16830
16831   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16832     RedrawGadget(game_gadget[i]);
16833 }
16834
16835 static void HandleGameButtons(struct GadgetInfo *gi)
16836 {
16837   int id = gi->custom_id;
16838
16839   if (game_status != GAME_MODE_PLAYING)
16840     return;
16841
16842   switch (id)
16843   {
16844     case GAME_CTRL_ID_STOP:
16845       if (tape.playing)
16846         TapeStop();
16847       else
16848         RequestQuitGame(TRUE);
16849       break;
16850
16851     case GAME_CTRL_ID_PAUSE:
16852       if (options.network)
16853       {
16854 #if defined(NETWORK_AVALIABLE)
16855         if (tape.pausing)
16856           SendToServer_ContinuePlaying();
16857         else
16858           SendToServer_PausePlaying();
16859 #endif
16860       }
16861       else
16862         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16863       break;
16864
16865     case GAME_CTRL_ID_PLAY:
16866       if (tape.pausing)
16867       {
16868 #if defined(NETWORK_AVALIABLE)
16869         if (options.network)
16870           SendToServer_ContinuePlaying();
16871         else
16872 #endif
16873         {
16874           tape.pausing = FALSE;
16875           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16876         }
16877       }
16878       break;
16879
16880     case SOUND_CTRL_ID_MUSIC:
16881       if (setup.sound_music)
16882       { 
16883         setup.sound_music = FALSE;
16884         FadeMusic();
16885       }
16886       else if (audio.music_available)
16887       { 
16888         setup.sound = setup.sound_music = TRUE;
16889
16890         SetAudioMode(setup.sound);
16891
16892         PlayLevelMusic();
16893       }
16894       break;
16895
16896     case SOUND_CTRL_ID_LOOPS:
16897       if (setup.sound_loops)
16898         setup.sound_loops = FALSE;
16899       else if (audio.loops_available)
16900       {
16901         setup.sound = setup.sound_loops = TRUE;
16902         SetAudioMode(setup.sound);
16903       }
16904       break;
16905
16906     case SOUND_CTRL_ID_SIMPLE:
16907       if (setup.sound_simple)
16908         setup.sound_simple = FALSE;
16909       else if (audio.sound_available)
16910       {
16911         setup.sound = setup.sound_simple = TRUE;
16912         SetAudioMode(setup.sound);
16913       }
16914       break;
16915
16916     default:
16917       break;
16918   }
16919 }