rnd-20070312-3-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
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #if 1
91 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
95 #else
96 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p)           ((p).y)
99 #endif
100
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
117 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
118
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1               (DX + XX_LEVEL1)
121 #define DX_LEVEL2               (DX + XX_LEVEL2)
122 #define DX_LEVEL                (DX + XX_LEVEL)
123 #define DY_LEVEL                (DY + YY_LEVEL)
124 #define DX_EMERALDS             (DX + XX_EMERALDS)
125 #define DY_EMERALDS             (DY + YY_EMERALDS)
126 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
128 #define DX_KEYS                 (DX + XX_KEYS)
129 #define DY_KEYS                 (DY + YY_KEYS)
130 #define DX_SCORE                (DX + XX_SCORE)
131 #define DY_SCORE                (DY + YY_SCORE)
132 #define DX_TIME1                (DX + XX_TIME1)
133 #define DX_TIME2                (DX + XX_TIME2)
134 #define DX_TIME                 (DX + XX_TIME)
135 #define DY_TIME                 (DY + YY_TIME)
136
137 #if 0
138 /* game panel display and control definitions */
139
140 #define GAME_CONTROL_LEVEL                      0
141 #define GAME_CONTROL_GEMS                       1
142 #define GAME_CONTROL_INVENTORY                  2
143 #define GAME_CONTROL_KEYS                       3
144 #define GAME_CONTROL_SCORE                      4
145 #define GAME_CONTROL_TIME                       5
146
147
148
149 #define GAME_CONTROL_LEVELS                     1
150 #define GAME_CONTROL_SCORES                     2
151 #define GAME_CONTROL_EDITOR                     3
152 #define GAME_CONTROL_INFO                       4
153 #define GAME_CONTROL_GAME                       5
154 #define GAME_CONTROL_SETUP                      6
155 #define GAME_CONTROL_QUIT                       7
156 #define GAME_CONTROL_PREV_LEVEL                 8
157 #define GAME_CONTROL_NEXT_LEVEL                 9
158 #define GAME_CONTROL_CURRENT_LEVEL              10
159 #define GAME_CONTROL_FIRST_LEVEL                11
160 #define GAME_CONTROL_LAST_LEVEL                 12
161 #define GAME_CONTROL_LEVEL_INFO_1               13
162 #define GAME_CONTROL_LEVEL_INFO_2               14
163 #define GAME_CONTROL_LEVEL_NAME                 15
164 #define GAME_CONTROL_LEVEL_AUTHOR               16
165 #define GAME_CONTROL_LEVEL_YEAR                 17
166 #define GAME_CONTROL_LEVEL_IMPORTED_FROM        18
167 #define GAME_CONTROL_LEVEL_IMPORTED_BY          19
168 #define GAME_CONTROL_LEVEL_TESTED_BY            20
169 #define GAME_CONTROL_TITLE_1                    21
170 #define GAME_CONTROL_TITLE_2                    22
171 #define GAME_CONTROL_TITLE_3                    23
172
173 static char str_game_text_name[10];
174 static char str_game_text_current_level[10];
175 static char str_game_text_first_level[10];
176 static char str_game_text_last_level[10];
177
178 static char *game_text_name                     = str_game_text_name;
179 static char *game_text_current_level            = str_game_text_current_level;
180 static char *game_text_first_level              = str_game_text_first_level;
181 static char *game_text_last_level               = str_game_text_last_level;
182 static char *game_text_levels                   = "Levelset";
183 static char *game_text_scores                   = "Hall Of Fame";
184 static char *game_text_editor                   = "Level Creator";
185 static char *game_text_info                     = "Info Screen";
186 static char *game_text_game                     = "Start Game";
187 static char *game_text_setup                    = "Setup";
188 static char *game_text_quit                     = "Quit";
189 static char *game_text_level_name               = level.name;
190 static char *game_text_level_author             = level.author;
191 static char *game_text_level_year               = NULL;
192 static char *game_text_level_imported_from      = NULL;
193 static char *game_text_level_imported_by        = NULL;
194 static char *game_text_level_tested_by          = NULL;
195 static char *game_text_title_1                  = PROGRAM_TITLE_STRING;
196 static char *game_text_title_2                  = PROGRAM_COPYRIGHT_STRING;
197 static char *game_text_title_3                  = PROGRAM_GAME_BY_STRING;
198
199 struct GameControlInfo
200 {
201   int nr;
202
203   struct MenuPosInfo *pos_button;
204   int button_graphic;
205
206   struct TextPosInfo *pos_text;
207   char **text;
208
209   struct TextPosInfo *pos_input;
210   char **input;
211 };
212
213 static struct GameControlInfo game_controls[] =
214 {
215   {
216     GAME_CONTROL_NAME,
217     &menu.game.button.name,             IMG_MENU_BUTTON,
218     &menu.game.text.name,               &game_text_name,
219     &menu.game.input.name,              &setup.player_name,
220   },
221   {
222     GAME_CONTROL_LEVELS,
223     &menu.game.button.levels,           IMG_MENU_BUTTON_ENTER_MENU,
224     &menu.game.text.levels,             &game_text_levels,
225     NULL,                               NULL,
226   },
227   {
228     GAME_CONTROL_SCORES,
229     &menu.game.button.scores,           IMG_MENU_BUTTON,
230     &menu.game.text.scores,             &game_text_scores,
231     NULL,                               NULL,
232   },
233   {
234     GAME_CONTROL_EDITOR,
235     &menu.game.button.editor,           IMG_MENU_BUTTON,
236     &menu.game.text.editor,             &game_text_editor,
237     NULL,                               NULL,
238   },
239   {
240     GAME_CONTROL_INFO,
241     &menu.game.button.info,             IMG_MENU_BUTTON_ENTER_MENU,
242     &menu.game.text.info,               &game_text_info,
243     NULL,                               NULL,
244   },
245   {
246     GAME_CONTROL_GAME,
247     &menu.game.button.game,             IMG_MENU_BUTTON,
248     &menu.game.text.game,               &game_text_game,
249     NULL,                               NULL,
250   },
251   {
252     GAME_CONTROL_SETUP,
253     &menu.game.button.setup,            IMG_MENU_BUTTON_ENTER_MENU,
254     &menu.game.text.setup,              &game_text_setup,
255     NULL,                               NULL,
256   },
257   {
258     GAME_CONTROL_QUIT,
259     &menu.game.button.quit,             IMG_MENU_BUTTON,
260     &menu.game.text.quit,               &game_text_quit,
261     NULL,                               NULL,
262   },
263 #if 0
264   /* (these two buttons are real gadgets) */
265   {
266     GAME_CONTROL_PREV_LEVEL,
267     &menu.game.button.prev_level,       IMG_MENU_BUTTON_PREV_LEVEL,
268     NULL,                               NULL,
269     NULL,                               NULL,
270   },
271   {
272     GAME_CONTROL_NEXT_LEVEL,
273     &menu.game.button.next_level,       IMG_MENU_BUTTON_NEXT_LEVEL,
274     NULL,                               NULL,
275     NULL,                               NULL,
276   },
277 #endif
278   {
279     GAME_CONTROL_CURRENT_LEVEL,
280     NULL,                               -1,
281     &menu.game.text.current_level,      &game_text_current_level,
282     NULL,                               NULL,
283   },
284   {
285     GAME_CONTROL_FIRST_LEVEL,
286     NULL,                               -1,
287     &menu.game.text.first_level,        &game_text_first_level,
288     NULL,                               NULL,
289   },
290   {
291     GAME_CONTROL_LAST_LEVEL,
292     NULL,                               -1,
293     &menu.game.text.last_level,         &game_text_last_level,
294     NULL,                               NULL,
295   },
296   {
297     GAME_CONTROL_LEVEL_INFO_1,
298     NULL,                               -1,
299     &menu.game.text.level_info_1,       NULL,
300     NULL,                               NULL,
301   },
302   {
303     GAME_CONTROL_LEVEL_INFO_2,
304     NULL,                               -1,
305     &menu.game.text.level_info_2,       NULL,
306     NULL,                               NULL,
307   },
308   {
309     GAME_CONTROL_LEVEL_NAME,
310     NULL,                               -1,
311     &menu.game.text.level_name,         &game_text_level_name,
312     NULL,                               NULL,
313   },
314   {
315     GAME_CONTROL_LEVEL_AUTHOR,
316     NULL,                               -1,
317     &menu.game.text.level_author,       &game_text_level_author,
318     NULL,                               NULL,
319   },
320   {
321     GAME_CONTROL_LEVEL_YEAR,
322     NULL,                               -1,
323     &menu.game.text.level_year,         &game_text_level_year,
324     NULL,                               NULL,
325   },
326   {
327     GAME_CONTROL_LEVEL_IMPORTED_FROM,
328     NULL,                               -1,
329     &menu.game.text.level_imported_from, &game_text_level_imported_from,
330     NULL,                               NULL,
331   },
332   {
333     GAME_CONTROL_LEVEL_IMPORTED_BY,
334     NULL,                               -1,
335     &menu.game.text.level_imported_by,  &game_text_level_imported_by,
336     NULL,                               NULL,
337   },
338   {
339     GAME_CONTROL_LEVEL_TESTED_BY,
340     NULL,                               -1,
341     &menu.game.text.level_tested_by,    &game_text_level_tested_by,
342     NULL,                               NULL,
343   },
344   {
345     GAME_CONTROL_TITLE_1,
346     NULL,                               -1,
347     &menu.game.text.title_1,            &game_text_title_1,
348     NULL,                               NULL,
349   },
350   {
351     GAME_CONTROL_TITLE_2,
352     NULL,                               -1,
353     &menu.game.text.title_2,            &game_text_title_2,
354     NULL,                               NULL,
355   },
356   {
357     GAME_CONTROL_TITLE_3,
358     NULL,                               -1,
359     &menu.game.text.title_3,            &game_text_title_3,
360     NULL,                               NULL,
361   },
362
363   {
364     -1,
365     NULL,                               -1,
366     NULL,                               NULL,
367     NULL,                               NULL,
368   }
369 };
370 #endif
371
372
373 /* values for delayed check of falling and moving elements and for collision */
374 #define CHECK_DELAY_MOVING      3
375 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
376 #define CHECK_DELAY_COLLISION   2
377 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
378
379 /* values for initial player move delay (initial delay counter value) */
380 #define INITIAL_MOVE_DELAY_OFF  -1
381 #define INITIAL_MOVE_DELAY_ON   0
382
383 /* values for player movement speed (which is in fact a delay value) */
384 #define MOVE_DELAY_MIN_SPEED    32
385 #define MOVE_DELAY_NORMAL_SPEED 8
386 #define MOVE_DELAY_HIGH_SPEED   4
387 #define MOVE_DELAY_MAX_SPEED    1
388
389 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
390 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
391
392 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
393 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
394
395 /* values for other actions */
396 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
397 #define MOVE_STEPSIZE_MIN       (1)
398 #define MOVE_STEPSIZE_MAX       (TILEX)
399
400 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
401 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
402
403 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
404
405 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
406                                  RND(element_info[e].push_delay_random))
407 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
408                                  RND(element_info[e].drop_delay_random))
409 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
410                                  RND(element_info[e].move_delay_random))
411 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
412                                     (element_info[e].move_delay_random))
413 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
414                                  RND(element_info[e].ce_value_random_initial))
415 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
416 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
417                                  RND((c)->delay_random * (c)->delay_frames))
418 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
419                                  RND((c)->delay_random))
420
421
422 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
423          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
424
425 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
426         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
427          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
428          (be) + (e) - EL_SELF)
429
430 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
431         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
432          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
433          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
434          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
435          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
436          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
437          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
438          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
439          (e))
440
441 #define CAN_GROW_INTO(e)                                                \
442         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
443
444 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
445                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
446                                         (condition)))
447
448 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
449                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
450                                         (CAN_MOVE_INTO_ACID(e) &&       \
451                                          Feld[x][y] == EL_ACID) ||      \
452                                         (condition)))
453
454 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
455                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
456                                         (CAN_MOVE_INTO_ACID(e) &&       \
457                                          Feld[x][y] == EL_ACID) ||      \
458                                         (condition)))
459
460 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
461                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
462                                         (condition) ||                  \
463                                         (CAN_MOVE_INTO_ACID(e) &&       \
464                                          Feld[x][y] == EL_ACID) ||      \
465                                         (DONT_COLLIDE_WITH(e) &&        \
466                                          IS_PLAYER(x, y) &&             \
467                                          !PLAYER_ENEMY_PROTECTED(x, y))))
468
469 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
470         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
471
472 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
473         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
474
475 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
476         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
477
478 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
479         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
480                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
481
482 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
483         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
484
485 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
486         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
487
488 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
489         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
490
491 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
492         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
493
494 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
495         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
496
497 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
498         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
499                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
500                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
501                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
502                                                  IS_FOOD_PENGUIN(Feld[x][y])))
503 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
504         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
505
506 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
507         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
508
509 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
510         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
511
512 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
513         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
514                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
515
516 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
517
518 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
519                 (!IS_PLAYER(x, y) &&                                    \
520                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
521
522 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
523         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
524
525 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
526 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
527
528 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
529 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
530 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
531 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
532
533 /* game button identifiers */
534 #define GAME_CTRL_ID_STOP               0
535 #define GAME_CTRL_ID_PAUSE              1
536 #define GAME_CTRL_ID_PLAY               2
537 #define SOUND_CTRL_ID_MUSIC             3
538 #define SOUND_CTRL_ID_LOOPS             4
539 #define SOUND_CTRL_ID_SIMPLE            5
540
541 #define NUM_GAME_BUTTONS                6
542
543
544 /* forward declaration for internal use */
545
546 static void CreateField(int, int, int);
547
548 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
549 static void AdvanceFrameAndPlayerCounters(int);
550
551 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
552 static boolean MovePlayer(struct PlayerInfo *, int, int);
553 static void ScrollPlayer(struct PlayerInfo *, int);
554 static void ScrollScreen(struct PlayerInfo *, int);
555
556 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
557
558 static void InitBeltMovement(void);
559 static void CloseAllOpenTimegates(void);
560 static void CheckGravityMovement(struct PlayerInfo *);
561 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
562 static void KillPlayerUnlessEnemyProtected(int, int);
563 static void KillPlayerUnlessExplosionProtected(int, int);
564
565 static void TestIfPlayerTouchesCustomElement(int, int);
566 static void TestIfElementTouchesCustomElement(int, int);
567 static void TestIfElementHitsCustomElement(int, int, int);
568 #if 0
569 static void TestIfElementSmashesCustomElement(int, int, int);
570 #endif
571
572 static void HandleElementChange(int, int, int);
573 static void ExecuteCustomElementAction(int, int, int, int);
574 static boolean ChangeElement(int, int, int, int);
575
576 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
577 #define CheckTriggeredElementChange(x, y, e, ev)                        \
578         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
579 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
580         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
581 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
582         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
583 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
584         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
585
586 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
587 #define CheckElementChange(x, y, e, te, ev)                             \
588         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
589 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
590         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
591 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
592         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
593
594 static void PlayLevelSound(int, int, int);
595 static void PlayLevelSoundNearest(int, int, int);
596 static void PlayLevelSoundAction(int, int, int);
597 static void PlayLevelSoundElementAction(int, int, int, int);
598 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
599 static void PlayLevelSoundActionIfLoop(int, int, int);
600 static void StopLevelSoundActionIfLoop(int, int, int);
601 static void PlayLevelMusic();
602
603 static void MapGameButtons();
604 static void HandleGameButtons(struct GadgetInfo *);
605
606 int AmoebeNachbarNr(int, int);
607 void AmoebeUmwandeln(int, int);
608 void ContinueMoving(int, int);
609 void Bang(int, int);
610 void InitMovDir(int, int);
611 void InitAmoebaNr(int, int);
612 int NewHiScore(void);
613
614 void TestIfGoodThingHitsBadThing(int, int, int);
615 void TestIfBadThingHitsGoodThing(int, int, int);
616 void TestIfPlayerTouchesBadThing(int, int);
617 void TestIfPlayerRunsIntoBadThing(int, int, int);
618 void TestIfBadThingTouchesPlayer(int, int);
619 void TestIfBadThingRunsIntoPlayer(int, int, int);
620 void TestIfFriendTouchesBadThing(int, int);
621 void TestIfBadThingTouchesFriend(int, int);
622 void TestIfBadThingTouchesOtherBadThing(int, int);
623
624 void KillPlayer(struct PlayerInfo *);
625 void BuryPlayer(struct PlayerInfo *);
626 void RemovePlayer(struct PlayerInfo *);
627
628 boolean SnapField(struct PlayerInfo *, int, int);
629 boolean DropElement(struct PlayerInfo *);
630
631 static int getInvisibleActiveFromInvisibleElement(int);
632 static int getInvisibleFromInvisibleActiveElement(int);
633
634 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
635
636 /* for detection of endless loops, caused by custom element programming */
637 /* (using maximal playfield width x 10 is just a rough approximation) */
638 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
639
640 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
641 {                                                                       \
642   if (recursion_loop_detected)                                          \
643     return (rc);                                                        \
644                                                                         \
645   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
646   {                                                                     \
647     recursion_loop_detected = TRUE;                                     \
648     recursion_loop_element = (e);                                       \
649   }                                                                     \
650                                                                         \
651   recursion_loop_depth++;                                               \
652 }
653
654 #define RECURSION_LOOP_DETECTION_END()                                  \
655 {                                                                       \
656   recursion_loop_depth--;                                               \
657 }
658
659 static int recursion_loop_depth;
660 static boolean recursion_loop_detected;
661 static boolean recursion_loop_element;
662
663
664 /* ------------------------------------------------------------------------- */
665 /* definition of elements that automatically change to other elements after  */
666 /* a specified time, eventually calling a function when changing             */
667 /* ------------------------------------------------------------------------- */
668
669 /* forward declaration for changer functions */
670 static void InitBuggyBase(int, int);
671 static void WarnBuggyBase(int, int);
672
673 static void InitTrap(int, int);
674 static void ActivateTrap(int, int);
675 static void ChangeActiveTrap(int, int);
676
677 static void InitRobotWheel(int, int);
678 static void RunRobotWheel(int, int);
679 static void StopRobotWheel(int, int);
680
681 static void InitTimegateWheel(int, int);
682 static void RunTimegateWheel(int, int);
683
684 static void InitMagicBallDelay(int, int);
685 static void ActivateMagicBall(int, int);
686
687 struct ChangingElementInfo
688 {
689   int element;
690   int target_element;
691   int change_delay;
692   void (*pre_change_function)(int x, int y);
693   void (*change_function)(int x, int y);
694   void (*post_change_function)(int x, int y);
695 };
696
697 static struct ChangingElementInfo change_delay_list[] =
698 {
699   {
700     EL_NUT_BREAKING,
701     EL_EMERALD,
702     6,
703     NULL,
704     NULL,
705     NULL
706   },
707   {
708     EL_PEARL_BREAKING,
709     EL_EMPTY,
710     8,
711     NULL,
712     NULL,
713     NULL
714   },
715   {
716     EL_EXIT_OPENING,
717     EL_EXIT_OPEN,
718     29,
719     NULL,
720     NULL,
721     NULL
722   },
723   {
724     EL_EXIT_CLOSING,
725     EL_EXIT_CLOSED,
726     29,
727     NULL,
728     NULL,
729     NULL
730   },
731   {
732     EL_STEEL_EXIT_OPENING,
733     EL_STEEL_EXIT_OPEN,
734     29,
735     NULL,
736     NULL,
737     NULL
738   },
739   {
740     EL_STEEL_EXIT_CLOSING,
741     EL_STEEL_EXIT_CLOSED,
742     29,
743     NULL,
744     NULL,
745     NULL
746   },
747   {
748     EL_EM_EXIT_OPENING,
749     EL_EM_EXIT_OPEN,
750     29,
751     NULL,
752     NULL,
753     NULL
754   },
755   {
756     EL_EM_EXIT_CLOSING,
757 #if 1
758     EL_EMPTY,
759 #else
760     EL_EM_EXIT_CLOSED,
761 #endif
762     29,
763     NULL,
764     NULL,
765     NULL
766   },
767   {
768     EL_EM_STEEL_EXIT_OPENING,
769     EL_EM_STEEL_EXIT_OPEN,
770     29,
771     NULL,
772     NULL,
773     NULL
774   },
775   {
776     EL_EM_STEEL_EXIT_CLOSING,
777 #if 1
778     EL_STEELWALL,
779 #else
780     EL_EM_STEEL_EXIT_CLOSED,
781 #endif
782     29,
783     NULL,
784     NULL,
785     NULL
786   },
787   {
788     EL_SP_EXIT_OPENING,
789     EL_SP_EXIT_OPEN,
790     29,
791     NULL,
792     NULL,
793     NULL
794   },
795   {
796     EL_SP_EXIT_CLOSING,
797     EL_SP_EXIT_CLOSED,
798     29,
799     NULL,
800     NULL,
801     NULL
802   },
803   {
804     EL_SWITCHGATE_OPENING,
805     EL_SWITCHGATE_OPEN,
806     29,
807     NULL,
808     NULL,
809     NULL
810   },
811   {
812     EL_SWITCHGATE_CLOSING,
813     EL_SWITCHGATE_CLOSED,
814     29,
815     NULL,
816     NULL,
817     NULL
818   },
819   {
820     EL_TIMEGATE_OPENING,
821     EL_TIMEGATE_OPEN,
822     29,
823     NULL,
824     NULL,
825     NULL
826   },
827   {
828     EL_TIMEGATE_CLOSING,
829     EL_TIMEGATE_CLOSED,
830     29,
831     NULL,
832     NULL,
833     NULL
834   },
835
836   {
837     EL_ACID_SPLASH_LEFT,
838     EL_EMPTY,
839     8,
840     NULL,
841     NULL,
842     NULL
843   },
844   {
845     EL_ACID_SPLASH_RIGHT,
846     EL_EMPTY,
847     8,
848     NULL,
849     NULL,
850     NULL
851   },
852   {
853     EL_SP_BUGGY_BASE,
854     EL_SP_BUGGY_BASE_ACTIVATING,
855     0,
856     InitBuggyBase,
857     NULL,
858     NULL
859   },
860   {
861     EL_SP_BUGGY_BASE_ACTIVATING,
862     EL_SP_BUGGY_BASE_ACTIVE,
863     0,
864     InitBuggyBase,
865     NULL,
866     NULL
867   },
868   {
869     EL_SP_BUGGY_BASE_ACTIVE,
870     EL_SP_BUGGY_BASE,
871     0,
872     InitBuggyBase,
873     WarnBuggyBase,
874     NULL
875   },
876   {
877     EL_TRAP,
878     EL_TRAP_ACTIVE,
879     0,
880     InitTrap,
881     NULL,
882     ActivateTrap
883   },
884   {
885     EL_TRAP_ACTIVE,
886     EL_TRAP,
887     31,
888     NULL,
889     ChangeActiveTrap,
890     NULL
891   },
892   {
893     EL_ROBOT_WHEEL_ACTIVE,
894     EL_ROBOT_WHEEL,
895     0,
896     InitRobotWheel,
897     RunRobotWheel,
898     StopRobotWheel
899   },
900   {
901     EL_TIMEGATE_SWITCH_ACTIVE,
902     EL_TIMEGATE_SWITCH,
903     0,
904     InitTimegateWheel,
905     RunTimegateWheel,
906     NULL
907   },
908   {
909     EL_DC_TIMEGATE_SWITCH_ACTIVE,
910     EL_DC_TIMEGATE_SWITCH,
911     0,
912     InitTimegateWheel,
913     RunTimegateWheel,
914     NULL
915   },
916   {
917     EL_EMC_MAGIC_BALL_ACTIVE,
918     EL_EMC_MAGIC_BALL_ACTIVE,
919     0,
920     InitMagicBallDelay,
921     NULL,
922     ActivateMagicBall
923   },
924   {
925     EL_EMC_SPRING_BUMPER_ACTIVE,
926     EL_EMC_SPRING_BUMPER,
927     8,
928     NULL,
929     NULL,
930     NULL
931   },
932   {
933     EL_DIAGONAL_SHRINKING,
934     EL_UNDEFINED,
935     0,
936     NULL,
937     NULL,
938     NULL
939   },
940   {
941     EL_DIAGONAL_GROWING,
942     EL_UNDEFINED,
943     0,
944     NULL,
945     NULL,
946     NULL,
947   },
948
949   {
950     EL_UNDEFINED,
951     EL_UNDEFINED,
952     -1,
953     NULL,
954     NULL,
955     NULL
956   }
957 };
958
959 struct
960 {
961   int element;
962   int push_delay_fixed, push_delay_random;
963 }
964 push_delay_list[] =
965 {
966   { EL_SPRING,                  0, 0 },
967   { EL_BALLOON,                 0, 0 },
968
969   { EL_SOKOBAN_OBJECT,          2, 0 },
970   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
971   { EL_SATELLITE,               2, 0 },
972   { EL_SP_DISK_YELLOW,          2, 0 },
973
974   { EL_UNDEFINED,               0, 0 },
975 };
976
977 struct
978 {
979   int element;
980   int move_stepsize;
981 }
982 move_stepsize_list[] =
983 {
984   { EL_AMOEBA_DROP,             2 },
985   { EL_AMOEBA_DROPPING,         2 },
986   { EL_QUICKSAND_FILLING,       1 },
987   { EL_QUICKSAND_EMPTYING,      1 },
988   { EL_QUICKSAND_FAST_FILLING,  2 },
989   { EL_QUICKSAND_FAST_EMPTYING, 2 },
990   { EL_MAGIC_WALL_FILLING,      2 },
991   { EL_MAGIC_WALL_EMPTYING,     2 },
992   { EL_BD_MAGIC_WALL_FILLING,   2 },
993   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
994   { EL_DC_MAGIC_WALL_FILLING,   2 },
995   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
996
997   { EL_UNDEFINED,               0 },
998 };
999
1000 struct
1001 {
1002   int element;
1003   int count;
1004 }
1005 collect_count_list[] =
1006 {
1007   { EL_EMERALD,                 1 },
1008   { EL_BD_DIAMOND,              1 },
1009   { EL_EMERALD_YELLOW,          1 },
1010   { EL_EMERALD_RED,             1 },
1011   { EL_EMERALD_PURPLE,          1 },
1012   { EL_DIAMOND,                 3 },
1013   { EL_SP_INFOTRON,             1 },
1014   { EL_PEARL,                   5 },
1015   { EL_CRYSTAL,                 8 },
1016
1017   { EL_UNDEFINED,               0 },
1018 };
1019
1020 struct
1021 {
1022   int element;
1023   int direction;
1024 }
1025 access_direction_list[] =
1026 {
1027   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1028   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1029   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1030   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1031   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1032   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1033   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1034   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1035   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1036   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1037   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1038
1039   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1040   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1041   { EL_SP_PORT_UP,                                                   MV_DOWN },
1042   { EL_SP_PORT_DOWN,                                         MV_UP           },
1043   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1044   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1045   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1046   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1047   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1048   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1049   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1050   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1051   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1052   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1053   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1054   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1055   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1056   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1057   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1058
1059   { EL_UNDEFINED,                       MV_NONE                              }
1060 };
1061
1062 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1063
1064 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1065 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1066 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1067                                  IS_JUST_CHANGING(x, y))
1068
1069 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1070
1071 /* static variables for playfield scan mode (scanning forward or backward) */
1072 static int playfield_scan_start_x = 0;
1073 static int playfield_scan_start_y = 0;
1074 static int playfield_scan_delta_x = 1;
1075 static int playfield_scan_delta_y = 1;
1076
1077 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1078                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1079                                      (y) += playfield_scan_delta_y)     \
1080                                 for ((x) = playfield_scan_start_x;      \
1081                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1082                                      (x) += playfield_scan_delta_x)
1083
1084 #ifdef DEBUG
1085 void DEBUG_SetMaximumDynamite()
1086 {
1087   int i;
1088
1089   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1090     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1091       local_player->inventory_element[local_player->inventory_size++] =
1092         EL_DYNAMITE;
1093 }
1094 #endif
1095
1096 static void InitPlayfieldScanModeVars()
1097 {
1098   if (game.use_reverse_scan_direction)
1099   {
1100     playfield_scan_start_x = lev_fieldx - 1;
1101     playfield_scan_start_y = lev_fieldy - 1;
1102
1103     playfield_scan_delta_x = -1;
1104     playfield_scan_delta_y = -1;
1105   }
1106   else
1107   {
1108     playfield_scan_start_x = 0;
1109     playfield_scan_start_y = 0;
1110
1111     playfield_scan_delta_x = 1;
1112     playfield_scan_delta_y = 1;
1113   }
1114 }
1115
1116 static void InitPlayfieldScanMode(int mode)
1117 {
1118   game.use_reverse_scan_direction =
1119     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1120
1121   InitPlayfieldScanModeVars();
1122 }
1123
1124 static int get_move_delay_from_stepsize(int move_stepsize)
1125 {
1126   move_stepsize =
1127     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1128
1129   /* make sure that stepsize value is always a power of 2 */
1130   move_stepsize = (1 << log_2(move_stepsize));
1131
1132   return TILEX / move_stepsize;
1133 }
1134
1135 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1136                                boolean init_game)
1137 {
1138   int player_nr = player->index_nr;
1139   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1140   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1141
1142   /* do no immediately change move delay -- the player might just be moving */
1143   player->move_delay_value_next = move_delay;
1144
1145   /* information if player can move must be set separately */
1146   player->cannot_move = cannot_move;
1147
1148   if (init_game)
1149   {
1150     player->move_delay       = game.initial_move_delay[player_nr];
1151     player->move_delay_value = game.initial_move_delay_value[player_nr];
1152
1153     player->move_delay_value_next = -1;
1154
1155     player->move_delay_reset_counter = 0;
1156   }
1157 }
1158
1159 void GetPlayerConfig()
1160 {
1161   GameFrameDelay = setup.game_frame_delay;
1162
1163   if (!audio.sound_available)
1164     setup.sound_simple = FALSE;
1165
1166   if (!audio.loops_available)
1167     setup.sound_loops = FALSE;
1168
1169   if (!audio.music_available)
1170     setup.sound_music = FALSE;
1171
1172   if (!video.fullscreen_available)
1173     setup.fullscreen = FALSE;
1174
1175   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1176
1177   SetAudioMode(setup.sound);
1178   InitJoysticks();
1179 }
1180
1181 int GetElementFromGroupElement(int element)
1182 {
1183   if (IS_GROUP_ELEMENT(element))
1184   {
1185     struct ElementGroupInfo *group = element_info[element].group;
1186     int last_anim_random_frame = gfx.anim_random_frame;
1187     int element_pos;
1188
1189     if (group->choice_mode == ANIM_RANDOM)
1190       gfx.anim_random_frame = RND(group->num_elements_resolved);
1191
1192     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1193                                     group->choice_mode, 0,
1194                                     group->choice_pos);
1195
1196     if (group->choice_mode == ANIM_RANDOM)
1197       gfx.anim_random_frame = last_anim_random_frame;
1198
1199     group->choice_pos++;
1200
1201     element = group->element_resolved[element_pos];
1202   }
1203
1204   return element;
1205 }
1206
1207 static void InitPlayerField(int x, int y, int element, boolean init_game)
1208 {
1209   if (element == EL_SP_MURPHY)
1210   {
1211     if (init_game)
1212     {
1213       if (stored_player[0].present)
1214       {
1215         Feld[x][y] = EL_SP_MURPHY_CLONE;
1216
1217         return;
1218       }
1219       else
1220       {
1221         stored_player[0].use_murphy = TRUE;
1222
1223         if (!level.use_artwork_element[0])
1224           stored_player[0].artwork_element = EL_SP_MURPHY;
1225       }
1226
1227       Feld[x][y] = EL_PLAYER_1;
1228     }
1229   }
1230
1231   if (init_game)
1232   {
1233     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1234     int jx = player->jx, jy = player->jy;
1235
1236     player->present = TRUE;
1237
1238     player->block_last_field = (element == EL_SP_MURPHY ?
1239                                 level.sp_block_last_field :
1240                                 level.block_last_field);
1241
1242     /* ---------- initialize player's last field block delay --------------- */
1243
1244     /* always start with reliable default value (no adjustment needed) */
1245     player->block_delay_adjustment = 0;
1246
1247     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1248     if (player->block_last_field && element == EL_SP_MURPHY)
1249       player->block_delay_adjustment = 1;
1250
1251     /* special case 2: in game engines before 3.1.1, blocking was different */
1252     if (game.use_block_last_field_bug)
1253       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1254
1255     if (!options.network || player->connected)
1256     {
1257       player->active = TRUE;
1258
1259       /* remove potentially duplicate players */
1260       if (StorePlayer[jx][jy] == Feld[x][y])
1261         StorePlayer[jx][jy] = 0;
1262
1263       StorePlayer[x][y] = Feld[x][y];
1264
1265       if (options.debug)
1266       {
1267         printf("Player %d activated.\n", player->element_nr);
1268         printf("[Local player is %d and currently %s.]\n",
1269                local_player->element_nr,
1270                local_player->active ? "active" : "not active");
1271       }
1272     }
1273
1274     Feld[x][y] = EL_EMPTY;
1275
1276     player->jx = player->last_jx = x;
1277     player->jy = player->last_jy = y;
1278   }
1279 }
1280
1281 static void InitField(int x, int y, boolean init_game)
1282 {
1283   int element = Feld[x][y];
1284
1285   switch (element)
1286   {
1287     case EL_SP_MURPHY:
1288     case EL_PLAYER_1:
1289     case EL_PLAYER_2:
1290     case EL_PLAYER_3:
1291     case EL_PLAYER_4:
1292       InitPlayerField(x, y, element, init_game);
1293       break;
1294
1295     case EL_SOKOBAN_FIELD_PLAYER:
1296       element = Feld[x][y] = EL_PLAYER_1;
1297       InitField(x, y, init_game);
1298
1299       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1300       InitField(x, y, init_game);
1301       break;
1302
1303     case EL_SOKOBAN_FIELD_EMPTY:
1304       local_player->sokobanfields_still_needed++;
1305       break;
1306
1307     case EL_STONEBLOCK:
1308       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1309         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1310       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1311         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1312       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1313         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1314       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1315         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1316       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1317         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1318       break;
1319
1320     case EL_BUG:
1321     case EL_BUG_RIGHT:
1322     case EL_BUG_UP:
1323     case EL_BUG_LEFT:
1324     case EL_BUG_DOWN:
1325     case EL_SPACESHIP:
1326     case EL_SPACESHIP_RIGHT:
1327     case EL_SPACESHIP_UP:
1328     case EL_SPACESHIP_LEFT:
1329     case EL_SPACESHIP_DOWN:
1330     case EL_BD_BUTTERFLY:
1331     case EL_BD_BUTTERFLY_RIGHT:
1332     case EL_BD_BUTTERFLY_UP:
1333     case EL_BD_BUTTERFLY_LEFT:
1334     case EL_BD_BUTTERFLY_DOWN:
1335     case EL_BD_FIREFLY:
1336     case EL_BD_FIREFLY_RIGHT:
1337     case EL_BD_FIREFLY_UP:
1338     case EL_BD_FIREFLY_LEFT:
1339     case EL_BD_FIREFLY_DOWN:
1340     case EL_PACMAN_RIGHT:
1341     case EL_PACMAN_UP:
1342     case EL_PACMAN_LEFT:
1343     case EL_PACMAN_DOWN:
1344     case EL_YAMYAM:
1345     case EL_YAMYAM_LEFT:
1346     case EL_YAMYAM_RIGHT:
1347     case EL_YAMYAM_UP:
1348     case EL_YAMYAM_DOWN:
1349     case EL_DARK_YAMYAM:
1350     case EL_ROBOT:
1351     case EL_PACMAN:
1352     case EL_SP_SNIKSNAK:
1353     case EL_SP_ELECTRON:
1354     case EL_MOLE:
1355     case EL_MOLE_LEFT:
1356     case EL_MOLE_RIGHT:
1357     case EL_MOLE_UP:
1358     case EL_MOLE_DOWN:
1359       InitMovDir(x, y);
1360       break;
1361
1362     case EL_AMOEBA_FULL:
1363     case EL_BD_AMOEBA:
1364       InitAmoebaNr(x, y);
1365       break;
1366
1367     case EL_AMOEBA_DROP:
1368       if (y == lev_fieldy - 1)
1369       {
1370         Feld[x][y] = EL_AMOEBA_GROWING;
1371         Store[x][y] = EL_AMOEBA_WET;
1372       }
1373       break;
1374
1375     case EL_DYNAMITE_ACTIVE:
1376     case EL_SP_DISK_RED_ACTIVE:
1377     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1378     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1379     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1380     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1381       MovDelay[x][y] = 96;
1382       break;
1383
1384     case EL_EM_DYNAMITE_ACTIVE:
1385       MovDelay[x][y] = 32;
1386       break;
1387
1388     case EL_LAMP:
1389       local_player->lights_still_needed++;
1390       break;
1391
1392     case EL_PENGUIN:
1393       local_player->friends_still_needed++;
1394       break;
1395
1396     case EL_PIG:
1397     case EL_DRAGON:
1398       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1399       break;
1400
1401     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1402     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1403     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1404     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1405     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1406     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1407     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1408     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1409     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1410     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1411     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1412     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1413       if (init_game)
1414       {
1415         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1416         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1417         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1418
1419         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1420         {
1421           game.belt_dir[belt_nr] = belt_dir;
1422           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1423         }
1424         else    /* more than one switch -- set it like the first switch */
1425         {
1426           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1427         }
1428       }
1429       break;
1430
1431 #if !USE_BOTH_SWITCHGATE_SWITCHES
1432     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1433       if (init_game)
1434         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1435       break;
1436
1437     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1438       if (init_game)
1439         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1440       break;
1441 #endif
1442
1443     case EL_LIGHT_SWITCH_ACTIVE:
1444       if (init_game)
1445         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1446       break;
1447
1448     case EL_INVISIBLE_STEELWALL:
1449     case EL_INVISIBLE_WALL:
1450     case EL_INVISIBLE_SAND:
1451       if (game.light_time_left > 0 ||
1452           game.lenses_time_left > 0)
1453         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1454       break;
1455
1456     case EL_EMC_MAGIC_BALL:
1457       if (game.ball_state)
1458         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1459       break;
1460
1461     case EL_EMC_MAGIC_BALL_SWITCH:
1462       if (game.ball_state)
1463         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1464       break;
1465
1466     default:
1467       if (IS_CUSTOM_ELEMENT(element))
1468       {
1469         if (CAN_MOVE(element))
1470           InitMovDir(x, y);
1471
1472 #if USE_NEW_CUSTOM_VALUE
1473         if (!element_info[element].use_last_ce_value || init_game)
1474           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1475 #endif
1476       }
1477       else if (IS_GROUP_ELEMENT(element))
1478       {
1479         Feld[x][y] = GetElementFromGroupElement(element);
1480
1481         InitField(x, y, init_game);
1482       }
1483
1484       break;
1485   }
1486
1487   if (!init_game)
1488     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1489 }
1490
1491 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1492 {
1493   InitField(x, y, init_game);
1494
1495   /* not needed to call InitMovDir() -- already done by InitField()! */
1496   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1497       CAN_MOVE(Feld[x][y]))
1498     InitMovDir(x, y);
1499 }
1500
1501 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1502 {
1503   int old_element = Feld[x][y];
1504
1505   InitField(x, y, init_game);
1506
1507   /* not needed to call InitMovDir() -- already done by InitField()! */
1508   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1509       CAN_MOVE(old_element) &&
1510       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1511     InitMovDir(x, y);
1512
1513   /* this case is in fact a combination of not less than three bugs:
1514      first, it calls InitMovDir() for elements that can move, although this is
1515      already done by InitField(); then, it checks the element that was at this
1516      field _before_ the call to InitField() (which can change it); lastly, it
1517      was not called for "mole with direction" elements, which were treated as
1518      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1519   */
1520 }
1521
1522 #if 1
1523
1524 void DrawGameValue_Emeralds(int value)
1525 {
1526   struct TextPosInfo *pos = &game.panel.gems;
1527 #if 1
1528   int font_nr = pos->font;
1529 #else
1530   int font_nr = FONT_TEXT_2;
1531 #endif
1532   int font_width = getFontWidth(font_nr);
1533   int chars = pos->chars;
1534
1535   if (PANEL_DEACTIVATED(pos))
1536     return;
1537
1538   pos->width = chars * font_width;
1539
1540   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1541 }
1542
1543 void DrawGameValue_Dynamite(int value)
1544 {
1545   struct TextPosInfo *pos = &game.panel.inventory;
1546 #if 1
1547   int font_nr = pos->font;
1548 #else
1549   int font_nr = FONT_TEXT_2;
1550 #endif
1551   int font_width = getFontWidth(font_nr);
1552   int chars = pos->chars;
1553
1554   if (PANEL_DEACTIVATED(pos))
1555     return;
1556
1557   pos->width = chars * font_width;
1558
1559   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1560 }
1561
1562 void DrawGameValue_Score(int value)
1563 {
1564   struct TextPosInfo *pos = &game.panel.score;
1565 #if 1
1566   int font_nr = pos->font;
1567 #else
1568   int font_nr = FONT_TEXT_2;
1569 #endif
1570   int font_width = getFontWidth(font_nr);
1571   int chars = pos->chars;
1572
1573   if (PANEL_DEACTIVATED(pos))
1574     return;
1575
1576   pos->width = chars * font_width;
1577
1578   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1579 }
1580
1581 void DrawGameValue_Time(int value)
1582 {
1583   struct TextPosInfo *pos = &game.panel.time;
1584   static int last_value = -1;
1585   int chars1 = 3;
1586   int chars2 = 4;
1587   int chars = pos->chars;
1588 #if 1
1589   int font1_nr = pos->font;
1590   int font2_nr = pos->font_alt;
1591 #else
1592   int font1_nr = FONT_TEXT_2;
1593   int font2_nr = FONT_TEXT_1;
1594 #endif
1595   int font_nr = font1_nr;
1596   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1597
1598   if (PANEL_DEACTIVATED(pos))
1599     return;
1600
1601   if (use_dynamic_chars)                /* use dynamic number of chars */
1602   {
1603     chars   = (value < 1000 ? chars1   : chars2);
1604     font_nr = (value < 1000 ? font1_nr : font2_nr);
1605   }
1606
1607   /* clear background if value just changed its size (dynamic chars only) */
1608   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
1609   {
1610     int width1 = chars1 * getFontWidth(font1_nr);
1611     int width2 = chars2 * getFontWidth(font2_nr);
1612     int max_width = MAX(width1, width2);
1613     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1614
1615     pos->width = max_width;
1616
1617     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1618                                max_width, max_height);
1619   }
1620
1621   pos->width = chars * getFontWidth(font_nr);
1622
1623   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1624
1625   last_value = value;
1626 }
1627
1628 void DrawGameValue_Level(int value)
1629 {
1630   struct TextPosInfo *pos = &game.panel.level;
1631   int chars1 = 2;
1632   int chars2 = 3;
1633   int chars = pos->chars;
1634 #if 1
1635   int font1_nr = pos->font;
1636   int font2_nr = pos->font_alt;
1637 #else
1638   int font1_nr = FONT_TEXT_2;
1639   int font2_nr = FONT_TEXT_1;
1640 #endif
1641   int font_nr = font1_nr;
1642   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
1643
1644   if (PANEL_DEACTIVATED(pos))
1645     return;
1646
1647   if (use_dynamic_chars)                /* use dynamic number of chars */
1648   {
1649     chars   = (level_nr < 100 ? chars1   : chars2);
1650     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1651   }
1652
1653   pos->width = chars * getFontWidth(font_nr);
1654
1655   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
1656 }
1657
1658 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1659 {
1660   struct TextPosInfo *pos = &game.panel.keys;
1661   int base_key_graphic = EL_KEY_1;
1662   int i;
1663
1664   if (PANEL_DEACTIVATED(pos))
1665     return;
1666
1667   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1668     base_key_graphic = EL_EM_KEY_1;
1669
1670   pos->width = 4 * MINI_TILEX;
1671
1672   /* currently only 4 of 8 possible keys are displayed */
1673   for (i = 0; i < STD_NUM_KEYS; i++)
1674   {
1675     int src_x = DOOR_GFX_PAGEX5 + 18;
1676     int src_y = DOOR_GFX_PAGEY1 + 123;
1677     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1678     int dst_y = PANEL_YPOS(pos);
1679
1680     if (key[i])
1681       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1682     else
1683       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1684                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1685   }
1686 }
1687
1688 #else
1689
1690 void DrawGameValue_Emeralds(int value)
1691 {
1692   int font_nr = FONT_TEXT_2;
1693   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1694
1695   if (PANEL_DEACTIVATED(game.panel.gems))
1696     return;
1697
1698   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1699 }
1700
1701 void DrawGameValue_Dynamite(int value)
1702 {
1703   int font_nr = FONT_TEXT_2;
1704   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1705
1706   if (PANEL_DEACTIVATED(game.panel.inventory))
1707     return;
1708
1709   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1710 }
1711
1712 void DrawGameValue_Score(int value)
1713 {
1714   int font_nr = FONT_TEXT_2;
1715   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1716
1717   if (PANEL_DEACTIVATED(game.panel.score))
1718     return;
1719
1720   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1721 }
1722
1723 void DrawGameValue_Time(int value)
1724 {
1725   int font1_nr = FONT_TEXT_2;
1726 #if 1
1727   int font2_nr = FONT_TEXT_1;
1728 #else
1729   int font2_nr = FONT_LEVEL_NUMBER;
1730 #endif
1731   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1732   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1733
1734   if (PANEL_DEACTIVATED(game.panel.time))
1735     return;
1736
1737   /* clear background if value just changed its size */
1738   if (value == 999 || value == 1000)
1739     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1740
1741   if (value < 1000)
1742     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1743   else
1744     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1745 }
1746
1747 void DrawGameValue_Level(int value)
1748 {
1749   int font1_nr = FONT_TEXT_2;
1750 #if 1
1751   int font2_nr = FONT_TEXT_1;
1752 #else
1753   int font2_nr = FONT_LEVEL_NUMBER;
1754 #endif
1755
1756   if (PANEL_DEACTIVATED(game.panel.level))
1757     return;
1758
1759   if (level_nr < 100)
1760     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1761   else
1762     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1763 }
1764
1765 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1766 {
1767   int base_key_graphic = EL_KEY_1;
1768   int i;
1769
1770   if (PANEL_DEACTIVATED(game.panel.keys))
1771     return;
1772
1773   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1774     base_key_graphic = EL_EM_KEY_1;
1775
1776   /* currently only 4 of 8 possible keys are displayed */
1777   for (i = 0; i < STD_NUM_KEYS; i++)
1778   {
1779     int x = XX_KEYS + i * MINI_TILEX;
1780     int y = YY_KEYS;
1781
1782     if (key[i])
1783       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1784     else
1785       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1786                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1787   }
1788 }
1789
1790 #endif
1791
1792 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1793                        int key_bits)
1794 {
1795   int key[MAX_NUM_KEYS];
1796   int i;
1797
1798   /* prevent EM engine from updating time/score values parallel to GameWon() */
1799   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1800       local_player->LevelSolved)
1801     return;
1802
1803   for (i = 0; i < MAX_NUM_KEYS; i++)
1804     key[i] = key_bits & (1 << i);
1805
1806   DrawGameValue_Level(level_nr);
1807
1808   DrawGameValue_Emeralds(emeralds);
1809   DrawGameValue_Dynamite(dynamite);
1810   DrawGameValue_Score(score);
1811   DrawGameValue_Time(time);
1812
1813   DrawGameValue_Keys(key);
1814 }
1815
1816 void DrawGameDoorValues()
1817 {
1818   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1819   int dynamite_value = 0;
1820   int score_value = (local_player->LevelSolved ? local_player->score_final :
1821                      local_player->score);
1822   int gems_value = local_player->gems_still_needed;
1823   int key_bits = 0;
1824   int i, j;
1825
1826   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1827   {
1828     DrawGameDoorValues_EM();
1829
1830     return;
1831   }
1832
1833   if (game.centered_player_nr == -1)
1834   {
1835     for (i = 0; i < MAX_PLAYERS; i++)
1836     {
1837       for (j = 0; j < MAX_NUM_KEYS; j++)
1838         if (stored_player[i].key[j])
1839           key_bits |= (1 << j);
1840
1841       dynamite_value += stored_player[i].inventory_size;
1842     }
1843   }
1844   else
1845   {
1846     int player_nr = game.centered_player_nr;
1847
1848     for (i = 0; i < MAX_NUM_KEYS; i++)
1849       if (stored_player[player_nr].key[i])
1850         key_bits |= (1 << i);
1851
1852     dynamite_value = stored_player[player_nr].inventory_size;
1853   }
1854
1855   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1856                     key_bits);
1857 }
1858
1859
1860 /*
1861   =============================================================================
1862   InitGameEngine()
1863   -----------------------------------------------------------------------------
1864   initialize game engine due to level / tape version number
1865   =============================================================================
1866 */
1867
1868 static void InitGameEngine()
1869 {
1870   int i, j, k, l, x, y;
1871
1872   /* set game engine from tape file when re-playing, else from level file */
1873   game.engine_version = (tape.playing ? tape.engine_version :
1874                          level.game_version);
1875
1876   /* ---------------------------------------------------------------------- */
1877   /* set flags for bugs and changes according to active game engine version */
1878   /* ---------------------------------------------------------------------- */
1879
1880   /*
1881     Summary of bugfix/change:
1882     Fixed handling for custom elements that change when pushed by the player.
1883
1884     Fixed/changed in version:
1885     3.1.0
1886
1887     Description:
1888     Before 3.1.0, custom elements that "change when pushing" changed directly
1889     after the player started pushing them (until then handled in "DigField()").
1890     Since 3.1.0, these custom elements are not changed until the "pushing"
1891     move of the element is finished (now handled in "ContinueMoving()").
1892
1893     Affected levels/tapes:
1894     The first condition is generally needed for all levels/tapes before version
1895     3.1.0, which might use the old behaviour before it was changed; known tapes
1896     that are affected are some tapes from the level set "Walpurgis Gardens" by
1897     Jamie Cullen.
1898     The second condition is an exception from the above case and is needed for
1899     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1900     above (including some development versions of 3.1.0), but before it was
1901     known that this change would break tapes like the above and was fixed in
1902     3.1.1, so that the changed behaviour was active although the engine version
1903     while recording maybe was before 3.1.0. There is at least one tape that is
1904     affected by this exception, which is the tape for the one-level set "Bug
1905     Machine" by Juergen Bonhagen.
1906   */
1907
1908   game.use_change_when_pushing_bug =
1909     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1910      !(tape.playing &&
1911        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1912        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1913
1914   /*
1915     Summary of bugfix/change:
1916     Fixed handling for blocking the field the player leaves when moving.
1917
1918     Fixed/changed in version:
1919     3.1.1
1920
1921     Description:
1922     Before 3.1.1, when "block last field when moving" was enabled, the field
1923     the player is leaving when moving was blocked for the time of the move,
1924     and was directly unblocked afterwards. This resulted in the last field
1925     being blocked for exactly one less than the number of frames of one player
1926     move. Additionally, even when blocking was disabled, the last field was
1927     blocked for exactly one frame.
1928     Since 3.1.1, due to changes in player movement handling, the last field
1929     is not blocked at all when blocking is disabled. When blocking is enabled,
1930     the last field is blocked for exactly the number of frames of one player
1931     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1932     last field is blocked for exactly one more than the number of frames of
1933     one player move.
1934
1935     Affected levels/tapes:
1936     (!!! yet to be determined -- probably many !!!)
1937   */
1938
1939   game.use_block_last_field_bug =
1940     (game.engine_version < VERSION_IDENT(3,1,1,0));
1941
1942   /*
1943     Summary of bugfix/change:
1944     Changed behaviour of CE changes with multiple changes per single frame.
1945
1946     Fixed/changed in version:
1947     3.2.0-6
1948
1949     Description:
1950     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1951     This resulted in race conditions where CEs seem to behave strange in some
1952     situations (where triggered CE changes were just skipped because there was
1953     already a CE change on that tile in the playfield in that engine frame).
1954     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1955     (The number of changes per frame must be limited in any case, because else
1956     it is easily possible to define CE changes that would result in an infinite
1957     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1958     should be set large enough so that it would only be reached in cases where
1959     the corresponding CE change conditions run into a loop. Therefore, it seems
1960     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1961     maximal number of change pages for custom elements.)
1962
1963     Affected levels/tapes:
1964     Probably many.
1965   */
1966
1967 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1968   game.max_num_changes_per_frame = 1;
1969 #else
1970   game.max_num_changes_per_frame =
1971     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1972 #endif
1973
1974   /* ---------------------------------------------------------------------- */
1975
1976   /* default scan direction: scan playfield from top/left to bottom/right */
1977   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1978
1979   /* dynamically adjust element properties according to game engine version */
1980   InitElementPropertiesEngine(game.engine_version);
1981
1982 #if 0
1983   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1984   printf("          tape version == %06d [%s] [file: %06d]\n",
1985          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1986          tape.file_version);
1987   printf("       => game.engine_version == %06d\n", game.engine_version);
1988 #endif
1989
1990   /* ---------- initialize player's initial move delay --------------------- */
1991
1992   /* dynamically adjust player properties according to level information */
1993   for (i = 0; i < MAX_PLAYERS; i++)
1994     game.initial_move_delay_value[i] =
1995       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1996
1997   /* dynamically adjust player properties according to game engine version */
1998   for (i = 0; i < MAX_PLAYERS; i++)
1999     game.initial_move_delay[i] =
2000       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2001        game.initial_move_delay_value[i] : 0);
2002
2003   /* ---------- initialize player's initial push delay --------------------- */
2004
2005   /* dynamically adjust player properties according to game engine version */
2006   game.initial_push_delay_value =
2007     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2008
2009   /* ---------- initialize changing elements ------------------------------- */
2010
2011   /* initialize changing elements information */
2012   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2013   {
2014     struct ElementInfo *ei = &element_info[i];
2015
2016     /* this pointer might have been changed in the level editor */
2017     ei->change = &ei->change_page[0];
2018
2019     if (!IS_CUSTOM_ELEMENT(i))
2020     {
2021       ei->change->target_element = EL_EMPTY_SPACE;
2022       ei->change->delay_fixed = 0;
2023       ei->change->delay_random = 0;
2024       ei->change->delay_frames = 1;
2025     }
2026
2027     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2028     {
2029       ei->has_change_event[j] = FALSE;
2030
2031       ei->event_page_nr[j] = 0;
2032       ei->event_page[j] = &ei->change_page[0];
2033     }
2034   }
2035
2036   /* add changing elements from pre-defined list */
2037   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2038   {
2039     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2040     struct ElementInfo *ei = &element_info[ch_delay->element];
2041
2042     ei->change->target_element       = ch_delay->target_element;
2043     ei->change->delay_fixed          = ch_delay->change_delay;
2044
2045     ei->change->pre_change_function  = ch_delay->pre_change_function;
2046     ei->change->change_function      = ch_delay->change_function;
2047     ei->change->post_change_function = ch_delay->post_change_function;
2048
2049     ei->change->can_change = TRUE;
2050     ei->change->can_change_or_has_action = TRUE;
2051
2052     ei->has_change_event[CE_DELAY] = TRUE;
2053
2054     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2055     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2056   }
2057
2058   /* ---------- initialize internal run-time variables ------------- */
2059
2060   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2061   {
2062     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2063
2064     for (j = 0; j < ei->num_change_pages; j++)
2065     {
2066       ei->change_page[j].can_change_or_has_action =
2067         (ei->change_page[j].can_change |
2068          ei->change_page[j].has_action);
2069     }
2070   }
2071
2072   /* add change events from custom element configuration */
2073   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2074   {
2075     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2076
2077     for (j = 0; j < ei->num_change_pages; j++)
2078     {
2079       if (!ei->change_page[j].can_change_or_has_action)
2080         continue;
2081
2082       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2083       {
2084         /* only add event page for the first page found with this event */
2085         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2086         {
2087           ei->has_change_event[k] = TRUE;
2088
2089           ei->event_page_nr[k] = j;
2090           ei->event_page[k] = &ei->change_page[j];
2091         }
2092       }
2093     }
2094   }
2095
2096   /* ---------- initialize run-time trigger player and element ------------- */
2097
2098   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2099   {
2100     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2101
2102     for (j = 0; j < ei->num_change_pages; j++)
2103     {
2104       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2105       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2106       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2107       ei->change_page[j].actual_trigger_ce_value = 0;
2108       ei->change_page[j].actual_trigger_ce_score = 0;
2109     }
2110   }
2111
2112   /* ---------- initialize trigger events ---------------------------------- */
2113
2114   /* initialize trigger events information */
2115   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2116     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2117       trigger_events[i][j] = FALSE;
2118
2119   /* add trigger events from element change event properties */
2120   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2121   {
2122     struct ElementInfo *ei = &element_info[i];
2123
2124     for (j = 0; j < ei->num_change_pages; j++)
2125     {
2126       if (!ei->change_page[j].can_change_or_has_action)
2127         continue;
2128
2129       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2130       {
2131         int trigger_element = ei->change_page[j].trigger_element;
2132
2133         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2134         {
2135           if (ei->change_page[j].has_event[k])
2136           {
2137             if (IS_GROUP_ELEMENT(trigger_element))
2138             {
2139               struct ElementGroupInfo *group =
2140                 element_info[trigger_element].group;
2141
2142               for (l = 0; l < group->num_elements_resolved; l++)
2143                 trigger_events[group->element_resolved[l]][k] = TRUE;
2144             }
2145             else if (trigger_element == EL_ANY_ELEMENT)
2146               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2147                 trigger_events[l][k] = TRUE;
2148             else
2149               trigger_events[trigger_element][k] = TRUE;
2150           }
2151         }
2152       }
2153     }
2154   }
2155
2156   /* ---------- initialize push delay -------------------------------------- */
2157
2158   /* initialize push delay values to default */
2159   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2160   {
2161     if (!IS_CUSTOM_ELEMENT(i))
2162     {
2163       /* set default push delay values (corrected since version 3.0.7-1) */
2164       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2165       {
2166         element_info[i].push_delay_fixed = 2;
2167         element_info[i].push_delay_random = 8;
2168       }
2169       else
2170       {
2171         element_info[i].push_delay_fixed = 8;
2172         element_info[i].push_delay_random = 8;
2173       }
2174     }
2175   }
2176
2177   /* set push delay value for certain elements from pre-defined list */
2178   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2179   {
2180     int e = push_delay_list[i].element;
2181
2182     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2183     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2184   }
2185
2186   /* set push delay value for Supaplex elements for newer engine versions */
2187   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2188   {
2189     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2190     {
2191       if (IS_SP_ELEMENT(i))
2192       {
2193         /* set SP push delay to just enough to push under a falling zonk */
2194         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2195
2196         element_info[i].push_delay_fixed  = delay;
2197         element_info[i].push_delay_random = 0;
2198       }
2199     }
2200   }
2201
2202   /* ---------- initialize move stepsize ----------------------------------- */
2203
2204   /* initialize move stepsize values to default */
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     if (!IS_CUSTOM_ELEMENT(i))
2207       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2208
2209   /* set move stepsize value for certain elements from pre-defined list */
2210   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2211   {
2212     int e = move_stepsize_list[i].element;
2213
2214     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2215   }
2216
2217   /* ---------- initialize collect score ----------------------------------- */
2218
2219   /* initialize collect score values for custom elements from initial value */
2220   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2221     if (IS_CUSTOM_ELEMENT(i))
2222       element_info[i].collect_score = element_info[i].collect_score_initial;
2223
2224   /* ---------- initialize collect count ----------------------------------- */
2225
2226   /* initialize collect count values for non-custom elements */
2227   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2228     if (!IS_CUSTOM_ELEMENT(i))
2229       element_info[i].collect_count_initial = 0;
2230
2231   /* add collect count values for all elements from pre-defined list */
2232   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2233     element_info[collect_count_list[i].element].collect_count_initial =
2234       collect_count_list[i].count;
2235
2236   /* ---------- initialize access direction -------------------------------- */
2237
2238   /* initialize access direction values to default (access from every side) */
2239   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2240     if (!IS_CUSTOM_ELEMENT(i))
2241       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2242
2243   /* set access direction value for certain elements from pre-defined list */
2244   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2245     element_info[access_direction_list[i].element].access_direction =
2246       access_direction_list[i].direction;
2247
2248   /* ---------- initialize explosion content ------------------------------- */
2249   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2250   {
2251     if (IS_CUSTOM_ELEMENT(i))
2252       continue;
2253
2254     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2255     {
2256       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2257
2258       element_info[i].content.e[x][y] =
2259         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2260          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2261          i == EL_PLAYER_3 ? EL_EMERALD :
2262          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2263          i == EL_MOLE ? EL_EMERALD_RED :
2264          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2265          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2266          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2267          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2268          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2269          i == EL_WALL_EMERALD ? EL_EMERALD :
2270          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2271          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2272          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2273          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2274          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2275          i == EL_WALL_PEARL ? EL_PEARL :
2276          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2277          EL_EMPTY);
2278     }
2279   }
2280
2281   /* ---------- initialize recursion detection ------------------------------ */
2282   recursion_loop_depth = 0;
2283   recursion_loop_detected = FALSE;
2284   recursion_loop_element = EL_UNDEFINED;
2285 }
2286
2287 int get_num_special_action(int element, int action_first, int action_last)
2288 {
2289   int num_special_action = 0;
2290   int i, j;
2291
2292   for (i = action_first; i <= action_last; i++)
2293   {
2294     boolean found = FALSE;
2295
2296     for (j = 0; j < NUM_DIRECTIONS; j++)
2297       if (el_act_dir2img(element, i, j) !=
2298           el_act_dir2img(element, ACTION_DEFAULT, j))
2299         found = TRUE;
2300
2301     if (found)
2302       num_special_action++;
2303     else
2304       break;
2305   }
2306
2307   return num_special_action;
2308 }
2309
2310
2311 /*
2312   =============================================================================
2313   InitGame()
2314   -----------------------------------------------------------------------------
2315   initialize and start new game
2316   =============================================================================
2317 */
2318
2319 void InitGame()
2320 {
2321   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2322   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2323   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2324   boolean do_fading = (game_status == GAME_MODE_MAIN);
2325   int i, j, x, y;
2326
2327   game_status = GAME_MODE_PLAYING;
2328
2329   InitGameEngine();
2330
2331   /* don't play tapes over network */
2332   network_playing = (options.network && !tape.playing);
2333
2334   for (i = 0; i < MAX_PLAYERS; i++)
2335   {
2336     struct PlayerInfo *player = &stored_player[i];
2337
2338     player->index_nr = i;
2339     player->index_bit = (1 << i);
2340     player->element_nr = EL_PLAYER_1 + i;
2341
2342     player->present = FALSE;
2343     player->active = FALSE;
2344     player->killed = FALSE;
2345
2346     player->action = 0;
2347     player->effective_action = 0;
2348     player->programmed_action = 0;
2349
2350     player->score = 0;
2351     player->score_final = 0;
2352
2353     player->gems_still_needed = level.gems_needed;
2354     player->sokobanfields_still_needed = 0;
2355     player->lights_still_needed = 0;
2356     player->friends_still_needed = 0;
2357
2358     for (j = 0; j < MAX_NUM_KEYS; j++)
2359       player->key[j] = FALSE;
2360
2361     player->num_white_keys = 0;
2362
2363     player->dynabomb_count = 0;
2364     player->dynabomb_size = 1;
2365     player->dynabombs_left = 0;
2366     player->dynabomb_xl = FALSE;
2367
2368     player->MovDir = MV_NONE;
2369     player->MovPos = 0;
2370     player->GfxPos = 0;
2371     player->GfxDir = MV_NONE;
2372     player->GfxAction = ACTION_DEFAULT;
2373     player->Frame = 0;
2374     player->StepFrame = 0;
2375
2376     player->use_murphy = FALSE;
2377     player->artwork_element =
2378       (level.use_artwork_element[i] ? level.artwork_element[i] :
2379        player->element_nr);
2380
2381     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2382     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2383
2384     player->gravity = level.initial_player_gravity[i];
2385
2386     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2387
2388     player->actual_frame_counter = 0;
2389
2390     player->step_counter = 0;
2391
2392     player->last_move_dir = MV_NONE;
2393
2394     player->is_active = FALSE;
2395
2396     player->is_waiting = FALSE;
2397     player->is_moving = FALSE;
2398     player->is_auto_moving = FALSE;
2399     player->is_digging = FALSE;
2400     player->is_snapping = FALSE;
2401     player->is_collecting = FALSE;
2402     player->is_pushing = FALSE;
2403     player->is_switching = FALSE;
2404     player->is_dropping = FALSE;
2405     player->is_dropping_pressed = FALSE;
2406
2407     player->is_bored = FALSE;
2408     player->is_sleeping = FALSE;
2409
2410     player->frame_counter_bored = -1;
2411     player->frame_counter_sleeping = -1;
2412
2413     player->anim_delay_counter = 0;
2414     player->post_delay_counter = 0;
2415
2416     player->dir_waiting = MV_NONE;
2417     player->action_waiting = ACTION_DEFAULT;
2418     player->last_action_waiting = ACTION_DEFAULT;
2419     player->special_action_bored = ACTION_DEFAULT;
2420     player->special_action_sleeping = ACTION_DEFAULT;
2421
2422     player->switch_x = -1;
2423     player->switch_y = -1;
2424
2425     player->drop_x = -1;
2426     player->drop_y = -1;
2427
2428     player->show_envelope = 0;
2429
2430     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2431
2432     player->push_delay       = -1;      /* initialized when pushing starts */
2433     player->push_delay_value = game.initial_push_delay_value;
2434
2435     player->drop_delay = 0;
2436     player->drop_pressed_delay = 0;
2437
2438     player->last_jx = -1;
2439     player->last_jy = -1;
2440     player->jx = -1;
2441     player->jy = -1;
2442
2443     player->shield_normal_time_left = 0;
2444     player->shield_deadly_time_left = 0;
2445
2446     player->inventory_infinite_element = EL_UNDEFINED;
2447     player->inventory_size = 0;
2448
2449     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2450     SnapField(player, 0, 0);
2451
2452     player->LevelSolved = FALSE;
2453     player->GameOver = FALSE;
2454
2455     player->LevelSolved_GameWon = FALSE;
2456     player->LevelSolved_GameEnd = FALSE;
2457     player->LevelSolved_PanelOff = FALSE;
2458     player->LevelSolved_SaveTape = FALSE;
2459     player->LevelSolved_SaveScore = FALSE;
2460   }
2461
2462   network_player_action_received = FALSE;
2463
2464 #if defined(NETWORK_AVALIABLE)
2465   /* initial null action */
2466   if (network_playing)
2467     SendToServer_MovePlayer(MV_NONE);
2468 #endif
2469
2470   ZX = ZY = -1;
2471   ExitX = ExitY = -1;
2472
2473   FrameCounter = 0;
2474   TimeFrames = 0;
2475   TimePlayed = 0;
2476   TimeLeft = level.time;
2477   TapeTime = 0;
2478
2479   ScreenMovDir = MV_NONE;
2480   ScreenMovPos = 0;
2481   ScreenGfxPos = 0;
2482
2483   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2484
2485   AllPlayersGone = FALSE;
2486
2487   game.yamyam_content_nr = 0;
2488   game.magic_wall_active = FALSE;
2489   game.magic_wall_time_left = 0;
2490   game.light_time_left = 0;
2491   game.timegate_time_left = 0;
2492   game.switchgate_pos = 0;
2493   game.wind_direction = level.wind_direction_initial;
2494
2495 #if !USE_PLAYER_GRAVITY
2496   game.gravity = FALSE;
2497   game.explosions_delayed = TRUE;
2498 #endif
2499
2500   game.lenses_time_left = 0;
2501   game.magnify_time_left = 0;
2502
2503   game.ball_state = level.ball_state_initial;
2504   game.ball_content_nr = 0;
2505
2506   game.envelope_active = FALSE;
2507
2508   /* set focus to local player for network games, else to all players */
2509   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2510   game.centered_player_nr_next = game.centered_player_nr;
2511   game.set_centered_player = FALSE;
2512
2513   if (network_playing && tape.recording)
2514   {
2515     /* store client dependent player focus when recording network games */
2516     tape.centered_player_nr_next = game.centered_player_nr_next;
2517     tape.set_centered_player = TRUE;
2518   }
2519
2520   for (i = 0; i < NUM_BELTS; i++)
2521   {
2522     game.belt_dir[i] = MV_NONE;
2523     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2524   }
2525
2526   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2527     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2528
2529   SCAN_PLAYFIELD(x, y)
2530   {
2531     Feld[x][y] = level.field[x][y];
2532     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2533     ChangeDelay[x][y] = 0;
2534     ChangePage[x][y] = -1;
2535 #if USE_NEW_CUSTOM_VALUE
2536     CustomValue[x][y] = 0;              /* initialized in InitField() */
2537 #endif
2538     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2539     AmoebaNr[x][y] = 0;
2540     WasJustMoving[x][y] = 0;
2541     WasJustFalling[x][y] = 0;
2542     CheckCollision[x][y] = 0;
2543     CheckImpact[x][y] = 0;
2544     Stop[x][y] = FALSE;
2545     Pushed[x][y] = FALSE;
2546
2547     ChangeCount[x][y] = 0;
2548     ChangeEvent[x][y] = -1;
2549
2550     ExplodePhase[x][y] = 0;
2551     ExplodeDelay[x][y] = 0;
2552     ExplodeField[x][y] = EX_TYPE_NONE;
2553
2554     RunnerVisit[x][y] = 0;
2555     PlayerVisit[x][y] = 0;
2556
2557     GfxFrame[x][y] = 0;
2558     GfxRandom[x][y] = INIT_GFX_RANDOM();
2559     GfxElement[x][y] = EL_UNDEFINED;
2560     GfxAction[x][y] = ACTION_DEFAULT;
2561     GfxDir[x][y] = MV_NONE;
2562   }
2563
2564   SCAN_PLAYFIELD(x, y)
2565   {
2566     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2567       emulate_bd = FALSE;
2568     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2569       emulate_sb = FALSE;
2570     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2571       emulate_sp = FALSE;
2572
2573     InitField(x, y, TRUE);
2574   }
2575
2576   InitBeltMovement();
2577
2578   for (i = 0; i < MAX_PLAYERS; i++)
2579   {
2580     struct PlayerInfo *player = &stored_player[i];
2581
2582     /* set number of special actions for bored and sleeping animation */
2583     player->num_special_action_bored =
2584       get_num_special_action(player->artwork_element,
2585                              ACTION_BORING_1, ACTION_BORING_LAST);
2586     player->num_special_action_sleeping =
2587       get_num_special_action(player->artwork_element,
2588                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2589   }
2590
2591   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2592                     emulate_sb ? EMU_SOKOBAN :
2593                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2594
2595 #if USE_NEW_ALL_SLIPPERY
2596   /* initialize type of slippery elements */
2597   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2598   {
2599     if (!IS_CUSTOM_ELEMENT(i))
2600     {
2601       /* default: elements slip down either to the left or right randomly */
2602       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2603
2604       /* SP style elements prefer to slip down on the left side */
2605       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2606         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2607
2608       /* BD style elements prefer to slip down on the left side */
2609       if (game.emulation == EMU_BOULDERDASH)
2610         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2611     }
2612   }
2613 #endif
2614
2615   /* initialize explosion and ignition delay */
2616   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2617   {
2618     if (!IS_CUSTOM_ELEMENT(i))
2619     {
2620       int num_phase = 8;
2621       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2622                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2623                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2624       int last_phase = (num_phase + 1) * delay;
2625       int half_phase = (num_phase / 2) * delay;
2626
2627       element_info[i].explosion_delay = last_phase - 1;
2628       element_info[i].ignition_delay = half_phase;
2629
2630       if (i == EL_BLACK_ORB)
2631         element_info[i].ignition_delay = 1;
2632     }
2633
2634 #if 0
2635     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2636       element_info[i].explosion_delay = 1;
2637
2638     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2639       element_info[i].ignition_delay = 1;
2640 #endif
2641   }
2642
2643   /* correct non-moving belts to start moving left */
2644   for (i = 0; i < NUM_BELTS; i++)
2645     if (game.belt_dir[i] == MV_NONE)
2646       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2647
2648   /* check if any connected player was not found in playfield */
2649   for (i = 0; i < MAX_PLAYERS; i++)
2650   {
2651     struct PlayerInfo *player = &stored_player[i];
2652
2653     if (player->connected && !player->present)
2654     {
2655       for (j = 0; j < MAX_PLAYERS; j++)
2656       {
2657         struct PlayerInfo *some_player = &stored_player[j];
2658         int jx = some_player->jx, jy = some_player->jy;
2659
2660         /* assign first free player found that is present in the playfield */
2661         if (some_player->present && !some_player->connected)
2662         {
2663           player->present = TRUE;
2664           player->active = TRUE;
2665
2666           some_player->present = FALSE;
2667           some_player->active = FALSE;
2668
2669           player->artwork_element = some_player->artwork_element;
2670
2671           player->block_last_field       = some_player->block_last_field;
2672           player->block_delay_adjustment = some_player->block_delay_adjustment;
2673
2674           StorePlayer[jx][jy] = player->element_nr;
2675           player->jx = player->last_jx = jx;
2676           player->jy = player->last_jy = jy;
2677
2678           break;
2679         }
2680       }
2681     }
2682   }
2683
2684   if (tape.playing)
2685   {
2686     /* when playing a tape, eliminate all players who do not participate */
2687
2688     for (i = 0; i < MAX_PLAYERS; i++)
2689     {
2690       if (stored_player[i].active && !tape.player_participates[i])
2691       {
2692         struct PlayerInfo *player = &stored_player[i];
2693         int jx = player->jx, jy = player->jy;
2694
2695         player->active = FALSE;
2696         StorePlayer[jx][jy] = 0;
2697         Feld[jx][jy] = EL_EMPTY;
2698       }
2699     }
2700   }
2701   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2702   {
2703     /* when in single player mode, eliminate all but the first active player */
2704
2705     for (i = 0; i < MAX_PLAYERS; i++)
2706     {
2707       if (stored_player[i].active)
2708       {
2709         for (j = i + 1; j < MAX_PLAYERS; j++)
2710         {
2711           if (stored_player[j].active)
2712           {
2713             struct PlayerInfo *player = &stored_player[j];
2714             int jx = player->jx, jy = player->jy;
2715
2716             player->active = FALSE;
2717             player->present = FALSE;
2718
2719             StorePlayer[jx][jy] = 0;
2720             Feld[jx][jy] = EL_EMPTY;
2721           }
2722         }
2723       }
2724     }
2725   }
2726
2727   /* when recording the game, store which players take part in the game */
2728   if (tape.recording)
2729   {
2730     for (i = 0; i < MAX_PLAYERS; i++)
2731       if (stored_player[i].active)
2732         tape.player_participates[i] = TRUE;
2733   }
2734
2735   if (options.debug)
2736   {
2737     for (i = 0; i < MAX_PLAYERS; i++)
2738     {
2739       struct PlayerInfo *player = &stored_player[i];
2740
2741       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2742              i+1,
2743              player->present,
2744              player->connected,
2745              player->active);
2746       if (local_player == player)
2747         printf("Player  %d is local player.\n", i+1);
2748     }
2749   }
2750
2751   if (BorderElement == EL_EMPTY)
2752   {
2753     SBX_Left = 0;
2754     SBX_Right = lev_fieldx - SCR_FIELDX;
2755     SBY_Upper = 0;
2756     SBY_Lower = lev_fieldy - SCR_FIELDY;
2757   }
2758   else
2759   {
2760     SBX_Left = -1;
2761     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2762     SBY_Upper = -1;
2763     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2764   }
2765
2766   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2767     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2768
2769   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2770     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2771
2772   /* if local player not found, look for custom element that might create
2773      the player (make some assumptions about the right custom element) */
2774   if (!local_player->present)
2775   {
2776     int start_x = 0, start_y = 0;
2777     int found_rating = 0;
2778     int found_element = EL_UNDEFINED;
2779     int player_nr = local_player->index_nr;
2780
2781     SCAN_PLAYFIELD(x, y)
2782     {
2783       int element = Feld[x][y];
2784       int content;
2785       int xx, yy;
2786       boolean is_player;
2787
2788       if (level.use_start_element[player_nr] &&
2789           level.start_element[player_nr] == element &&
2790           found_rating < 4)
2791       {
2792         start_x = x;
2793         start_y = y;
2794
2795         found_rating = 4;
2796         found_element = element;
2797       }
2798
2799       if (!IS_CUSTOM_ELEMENT(element))
2800         continue;
2801
2802       if (CAN_CHANGE(element))
2803       {
2804         for (i = 0; i < element_info[element].num_change_pages; i++)
2805         {
2806           /* check for player created from custom element as single target */
2807           content = element_info[element].change_page[i].target_element;
2808           is_player = ELEM_IS_PLAYER(content);
2809
2810           if (is_player && (found_rating < 3 || element < found_element))
2811           {
2812             start_x = x;
2813             start_y = y;
2814
2815             found_rating = 3;
2816             found_element = element;
2817           }
2818         }
2819       }
2820
2821       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2822       {
2823         /* check for player created from custom element as explosion content */
2824         content = element_info[element].content.e[xx][yy];
2825         is_player = ELEM_IS_PLAYER(content);
2826
2827         if (is_player && (found_rating < 2 || element < found_element))
2828         {
2829           start_x = x + xx - 1;
2830           start_y = y + yy - 1;
2831
2832           found_rating = 2;
2833           found_element = element;
2834         }
2835
2836         if (!CAN_CHANGE(element))
2837           continue;
2838
2839         for (i = 0; i < element_info[element].num_change_pages; i++)
2840         {
2841           /* check for player created from custom element as extended target */
2842           content =
2843             element_info[element].change_page[i].target_content.e[xx][yy];
2844
2845           is_player = ELEM_IS_PLAYER(content);
2846
2847           if (is_player && (found_rating < 1 || element < found_element))
2848           {
2849             start_x = x + xx - 1;
2850             start_y = y + yy - 1;
2851
2852             found_rating = 1;
2853             found_element = element;
2854           }
2855         }
2856       }
2857     }
2858
2859     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2860                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2861                 start_x - MIDPOSX);
2862
2863     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2864                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2865                 start_y - MIDPOSY);
2866   }
2867   else
2868   {
2869     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2870                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2871                 local_player->jx - MIDPOSX);
2872
2873     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2874                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2875                 local_player->jy - MIDPOSY);
2876   }
2877
2878   StopAnimation();
2879
2880   if (!game.restart_level)
2881     CloseDoor(DOOR_CLOSE_1);
2882
2883   if (do_fading)
2884     FadeOut(REDRAW_FIELD);
2885
2886   /* !!! FIX THIS (START) !!! */
2887   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2888   {
2889     InitGameEngine_EM();
2890
2891     /* blit playfield from scroll buffer to normal back buffer for fading in */
2892     BlitScreenToBitmap_EM(backbuffer);
2893   }
2894   else
2895   {
2896     DrawLevel();
2897     DrawAllPlayers();
2898
2899     /* after drawing the level, correct some elements */
2900     if (game.timegate_time_left == 0)
2901       CloseAllOpenTimegates();
2902
2903     /* blit playfield from scroll buffer to normal back buffer for fading in */
2904     if (setup.soft_scrolling)
2905       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2906
2907     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2908   }
2909   /* !!! FIX THIS (END) !!! */
2910
2911   if (do_fading)
2912     FadeIn(REDRAW_FIELD);
2913
2914   BackToFront();
2915
2916   if (!game.restart_level)
2917   {
2918     /* copy default game door content to main double buffer */
2919     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2920                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2921   }
2922
2923   SetPanelBackground();
2924   SetDrawBackgroundMask(REDRAW_DOOR_1);
2925
2926   DrawGameDoorValues();
2927
2928   if (!game.restart_level)
2929   {
2930     UnmapGameButtons();
2931     UnmapTapeButtons();
2932     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2933     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2934     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2935     MapGameButtons();
2936     MapTapeButtons();
2937
2938     /* copy actual game door content to door double buffer for OpenDoor() */
2939     BlitBitmap(drawto, bitmap_db_door,
2940                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2941
2942     OpenDoor(DOOR_OPEN_ALL);
2943
2944     PlaySound(SND_GAME_STARTING);
2945
2946     if (setup.sound_music)
2947       PlayLevelMusic();
2948
2949     KeyboardAutoRepeatOffUnlessAutoplay();
2950
2951     if (options.debug)
2952     {
2953       for (i = 0; i < MAX_PLAYERS; i++)
2954         printf("Player %d %sactive.\n",
2955                i + 1, (stored_player[i].active ? "" : "not "));
2956     }
2957   }
2958
2959 #if 1
2960   UnmapAllGadgets();
2961
2962   MapGameButtons();
2963   MapTapeButtons();
2964 #endif
2965
2966   game.restart_level = FALSE;
2967 }
2968
2969 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2970 {
2971   /* this is used for non-R'n'D game engines to update certain engine values */
2972
2973   /* needed to determine if sounds are played within the visible screen area */
2974   scroll_x = actual_scroll_x;
2975   scroll_y = actual_scroll_y;
2976 }
2977
2978 void InitMovDir(int x, int y)
2979 {
2980   int i, element = Feld[x][y];
2981   static int xy[4][2] =
2982   {
2983     {  0, +1 },
2984     { +1,  0 },
2985     {  0, -1 },
2986     { -1,  0 }
2987   };
2988   static int direction[3][4] =
2989   {
2990     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2991     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2992     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2993   };
2994
2995   switch (element)
2996   {
2997     case EL_BUG_RIGHT:
2998     case EL_BUG_UP:
2999     case EL_BUG_LEFT:
3000     case EL_BUG_DOWN:
3001       Feld[x][y] = EL_BUG;
3002       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3003       break;
3004
3005     case EL_SPACESHIP_RIGHT:
3006     case EL_SPACESHIP_UP:
3007     case EL_SPACESHIP_LEFT:
3008     case EL_SPACESHIP_DOWN:
3009       Feld[x][y] = EL_SPACESHIP;
3010       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3011       break;
3012
3013     case EL_BD_BUTTERFLY_RIGHT:
3014     case EL_BD_BUTTERFLY_UP:
3015     case EL_BD_BUTTERFLY_LEFT:
3016     case EL_BD_BUTTERFLY_DOWN:
3017       Feld[x][y] = EL_BD_BUTTERFLY;
3018       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3019       break;
3020
3021     case EL_BD_FIREFLY_RIGHT:
3022     case EL_BD_FIREFLY_UP:
3023     case EL_BD_FIREFLY_LEFT:
3024     case EL_BD_FIREFLY_DOWN:
3025       Feld[x][y] = EL_BD_FIREFLY;
3026       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3027       break;
3028
3029     case EL_PACMAN_RIGHT:
3030     case EL_PACMAN_UP:
3031     case EL_PACMAN_LEFT:
3032     case EL_PACMAN_DOWN:
3033       Feld[x][y] = EL_PACMAN;
3034       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3035       break;
3036
3037     case EL_YAMYAM_LEFT:
3038     case EL_YAMYAM_RIGHT:
3039     case EL_YAMYAM_UP:
3040     case EL_YAMYAM_DOWN:
3041       Feld[x][y] = EL_YAMYAM;
3042       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3043       break;
3044
3045     case EL_SP_SNIKSNAK:
3046       MovDir[x][y] = MV_UP;
3047       break;
3048
3049     case EL_SP_ELECTRON:
3050       MovDir[x][y] = MV_LEFT;
3051       break;
3052
3053     case EL_MOLE_LEFT:
3054     case EL_MOLE_RIGHT:
3055     case EL_MOLE_UP:
3056     case EL_MOLE_DOWN:
3057       Feld[x][y] = EL_MOLE;
3058       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3059       break;
3060
3061     default:
3062       if (IS_CUSTOM_ELEMENT(element))
3063       {
3064         struct ElementInfo *ei = &element_info[element];
3065         int move_direction_initial = ei->move_direction_initial;
3066         int move_pattern = ei->move_pattern;
3067
3068         if (move_direction_initial == MV_START_PREVIOUS)
3069         {
3070           if (MovDir[x][y] != MV_NONE)
3071             return;
3072
3073           move_direction_initial = MV_START_AUTOMATIC;
3074         }
3075
3076         if (move_direction_initial == MV_START_RANDOM)
3077           MovDir[x][y] = 1 << RND(4);
3078         else if (move_direction_initial & MV_ANY_DIRECTION)
3079           MovDir[x][y] = move_direction_initial;
3080         else if (move_pattern == MV_ALL_DIRECTIONS ||
3081                  move_pattern == MV_TURNING_LEFT ||
3082                  move_pattern == MV_TURNING_RIGHT ||
3083                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3084                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3085                  move_pattern == MV_TURNING_RANDOM)
3086           MovDir[x][y] = 1 << RND(4);
3087         else if (move_pattern == MV_HORIZONTAL)
3088           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3089         else if (move_pattern == MV_VERTICAL)
3090           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3091         else if (move_pattern & MV_ANY_DIRECTION)
3092           MovDir[x][y] = element_info[element].move_pattern;
3093         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3094                  move_pattern == MV_ALONG_RIGHT_SIDE)
3095         {
3096           /* use random direction as default start direction */
3097           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3098             MovDir[x][y] = 1 << RND(4);
3099
3100           for (i = 0; i < NUM_DIRECTIONS; i++)
3101           {
3102             int x1 = x + xy[i][0];
3103             int y1 = y + xy[i][1];
3104
3105             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3106             {
3107               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3108                 MovDir[x][y] = direction[0][i];
3109               else
3110                 MovDir[x][y] = direction[1][i];
3111
3112               break;
3113             }
3114           }
3115         }                
3116       }
3117       else
3118       {
3119         MovDir[x][y] = 1 << RND(4);
3120
3121         if (element != EL_BUG &&
3122             element != EL_SPACESHIP &&
3123             element != EL_BD_BUTTERFLY &&
3124             element != EL_BD_FIREFLY)
3125           break;
3126
3127         for (i = 0; i < NUM_DIRECTIONS; i++)
3128         {
3129           int x1 = x + xy[i][0];
3130           int y1 = y + xy[i][1];
3131
3132           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3133           {
3134             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3135             {
3136               MovDir[x][y] = direction[0][i];
3137               break;
3138             }
3139             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3140                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3141             {
3142               MovDir[x][y] = direction[1][i];
3143               break;
3144             }
3145           }
3146         }
3147       }
3148       break;
3149   }
3150
3151   GfxDir[x][y] = MovDir[x][y];
3152 }
3153
3154 void InitAmoebaNr(int x, int y)
3155 {
3156   int i;
3157   int group_nr = AmoebeNachbarNr(x, y);
3158
3159   if (group_nr == 0)
3160   {
3161     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3162     {
3163       if (AmoebaCnt[i] == 0)
3164       {
3165         group_nr = i;
3166         break;
3167       }
3168     }
3169   }
3170
3171   AmoebaNr[x][y] = group_nr;
3172   AmoebaCnt[group_nr]++;
3173   AmoebaCnt2[group_nr]++;
3174 }
3175
3176 static void PlayerWins(struct PlayerInfo *player)
3177 {
3178   player->LevelSolved = TRUE;
3179   player->GameOver = TRUE;
3180
3181   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3182                          level.native_em_level->lev->score : player->score);
3183 }
3184
3185 void GameWon()
3186 {
3187   static int time, time_final;
3188   static int score, score_final;
3189   static int game_over_delay_1 = 0;
3190   static int game_over_delay_2 = 0;
3191   int game_over_delay_value_1 = 50;
3192   int game_over_delay_value_2 = 50;
3193
3194   if (!local_player->LevelSolved_GameWon)
3195   {
3196     int i;
3197
3198     /* do not start end game actions before the player stops moving (to exit) */
3199     if (local_player->MovPos)
3200       return;
3201
3202     local_player->LevelSolved_GameWon = TRUE;
3203     local_player->LevelSolved_SaveTape = tape.recording;
3204     local_player->LevelSolved_SaveScore = !tape.playing;
3205
3206     if (tape.auto_play)         /* tape might already be stopped here */
3207       tape.auto_play_level_solved = TRUE;
3208
3209 #if 1
3210     TapeStop();
3211 #endif
3212
3213     game_over_delay_1 = game_over_delay_value_1;
3214     game_over_delay_2 = game_over_delay_value_2;
3215
3216     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3217     score = score_final = local_player->score_final;
3218
3219     if (TimeLeft > 0)
3220     {
3221       time_final = 0;
3222       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3223     }
3224     else if (level.time == 0 && TimePlayed < 999)
3225     {
3226       time_final = 999;
3227       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3228     }
3229
3230     local_player->score_final = score_final;
3231
3232     if (level_editor_test_game)
3233     {
3234       time = time_final;
3235       score = score_final;
3236
3237       DrawGameValue_Time(time);
3238       DrawGameValue_Score(score);
3239     }
3240
3241     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3242     {
3243       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3244       {
3245         /* close exit door after last player */
3246         if ((AllPlayersGone &&
3247              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3248               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3249               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3250             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3251             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3252         {
3253           int element = Feld[ExitX][ExitY];
3254
3255 #if 0
3256           if (element == EL_EM_EXIT_OPEN ||
3257               element == EL_EM_STEEL_EXIT_OPEN)
3258           {
3259             Bang(ExitX, ExitY);
3260           }
3261           else
3262 #endif
3263           {
3264             Feld[ExitX][ExitY] =
3265               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3266                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3267                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3268                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3269                EL_EM_STEEL_EXIT_CLOSING);
3270
3271             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3272           }
3273         }
3274
3275         /* player disappears */
3276         DrawLevelField(ExitX, ExitY);
3277       }
3278
3279       for (i = 0; i < MAX_PLAYERS; i++)
3280       {
3281         struct PlayerInfo *player = &stored_player[i];
3282
3283         if (player->present)
3284         {
3285           RemovePlayer(player);
3286
3287           /* player disappears */
3288           DrawLevelField(player->jx, player->jy);
3289         }
3290       }
3291     }
3292
3293     PlaySound(SND_GAME_WINNING);
3294   }
3295
3296   if (game_over_delay_1 > 0)
3297   {
3298     game_over_delay_1--;
3299
3300     return;
3301   }
3302
3303   if (time != time_final)
3304   {
3305     int time_to_go = ABS(time_final - time);
3306     int time_count_dir = (time < time_final ? +1 : -1);
3307     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3308
3309     time  += time_count_steps * time_count_dir;
3310     score += time_count_steps * level.score[SC_TIME_BONUS];
3311
3312     DrawGameValue_Time(time);
3313     DrawGameValue_Score(score);
3314
3315     if (time == time_final)
3316       StopSound(SND_GAME_LEVELTIME_BONUS);
3317     else if (setup.sound_loops)
3318       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3319     else
3320       PlaySound(SND_GAME_LEVELTIME_BONUS);
3321
3322     return;
3323   }
3324
3325   local_player->LevelSolved_PanelOff = TRUE;
3326
3327   if (game_over_delay_2 > 0)
3328   {
3329     game_over_delay_2--;
3330
3331     return;
3332   }
3333
3334 #if 1
3335   GameEnd();
3336 #endif
3337 }
3338
3339 void GameEnd()
3340 {
3341   int hi_pos;
3342   boolean raise_level = FALSE;
3343
3344   local_player->LevelSolved_GameEnd = TRUE;
3345
3346   CloseDoor(DOOR_CLOSE_1);
3347
3348   if (local_player->LevelSolved_SaveTape)
3349   {
3350 #if 0
3351     TapeStop();
3352 #endif
3353
3354 #if 1
3355     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3356 #else
3357     SaveTape(tape.level_nr);            /* ask to save tape */
3358 #endif
3359   }
3360
3361   if (level_editor_test_game)
3362   {
3363     game_status = GAME_MODE_MAIN;
3364
3365     DrawMainMenu();
3366
3367     return;
3368   }
3369
3370   if (!local_player->LevelSolved_SaveScore)
3371   {
3372     FadeOut(REDRAW_FIELD);
3373
3374     game_status = GAME_MODE_MAIN;
3375
3376     DrawAndFadeInMainMenu(REDRAW_FIELD);
3377
3378     return;
3379   }
3380
3381   if (level_nr == leveldir_current->handicap_level)
3382   {
3383     leveldir_current->handicap_level++;
3384     SaveLevelSetup_SeriesInfo();
3385   }
3386
3387   if (level_nr < leveldir_current->last_level)
3388     raise_level = TRUE;                 /* advance to next level */
3389
3390   if ((hi_pos = NewHiScore()) >= 0) 
3391   {
3392     game_status = GAME_MODE_SCORES;
3393
3394     DrawHallOfFame(hi_pos);
3395
3396     if (raise_level)
3397     {
3398       level_nr++;
3399       TapeErase();
3400     }
3401   }
3402   else
3403   {
3404     FadeOut(REDRAW_FIELD);
3405
3406     game_status = GAME_MODE_MAIN;
3407
3408     if (raise_level)
3409     {
3410       level_nr++;
3411       TapeErase();
3412     }
3413
3414     DrawAndFadeInMainMenu(REDRAW_FIELD);
3415   }
3416 }
3417
3418 int NewHiScore()
3419 {
3420   int k, l;
3421   int position = -1;
3422
3423   LoadScore(level_nr);
3424
3425   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3426       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3427     return -1;
3428
3429   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3430   {
3431     if (local_player->score_final > highscore[k].Score)
3432     {
3433       /* player has made it to the hall of fame */
3434
3435       if (k < MAX_SCORE_ENTRIES - 1)
3436       {
3437         int m = MAX_SCORE_ENTRIES - 1;
3438
3439 #ifdef ONE_PER_NAME
3440         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3441           if (strEqual(setup.player_name, highscore[l].Name))
3442             m = l;
3443         if (m == k)     /* player's new highscore overwrites his old one */
3444           goto put_into_list;
3445 #endif
3446
3447         for (l = m; l > k; l--)
3448         {
3449           strcpy(highscore[l].Name, highscore[l - 1].Name);
3450           highscore[l].Score = highscore[l - 1].Score;
3451         }
3452       }
3453
3454 #ifdef ONE_PER_NAME
3455       put_into_list:
3456 #endif
3457       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3458       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3459       highscore[k].Score = local_player->score_final; 
3460       position = k;
3461       break;
3462     }
3463
3464 #ifdef ONE_PER_NAME
3465     else if (!strncmp(setup.player_name, highscore[k].Name,
3466                       MAX_PLAYER_NAME_LEN))
3467       break;    /* player already there with a higher score */
3468 #endif
3469
3470   }
3471
3472   if (position >= 0) 
3473     SaveScore(level_nr);
3474
3475   return position;
3476 }
3477
3478 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3479 {
3480   int element = Feld[x][y];
3481   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3482   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3483   int horiz_move = (dx != 0);
3484   int sign = (horiz_move ? dx : dy);
3485   int step = sign * element_info[element].move_stepsize;
3486
3487   /* special values for move stepsize for spring and things on conveyor belt */
3488   if (horiz_move)
3489   {
3490     if (CAN_FALL(element) &&
3491         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3492       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3493     else if (element == EL_SPRING)
3494       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3495   }
3496
3497   return step;
3498 }
3499
3500 inline static int getElementMoveStepsize(int x, int y)
3501 {
3502   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3503 }
3504
3505 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3506 {
3507   if (player->GfxAction != action || player->GfxDir != dir)
3508   {
3509 #if 0
3510     printf("Player frame reset! (%d => %d, %d => %d)\n",
3511            player->GfxAction, action, player->GfxDir, dir);
3512 #endif
3513
3514     player->GfxAction = action;
3515     player->GfxDir = dir;
3516     player->Frame = 0;
3517     player->StepFrame = 0;
3518   }
3519 }
3520
3521 #if USE_GFX_RESET_GFX_ANIMATION
3522 static void ResetGfxFrame(int x, int y, boolean redraw)
3523 {
3524   int element = Feld[x][y];
3525   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3526   int last_gfx_frame = GfxFrame[x][y];
3527
3528   if (graphic_info[graphic].anim_global_sync)
3529     GfxFrame[x][y] = FrameCounter;
3530   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3531     GfxFrame[x][y] = CustomValue[x][y];
3532   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3533     GfxFrame[x][y] = element_info[element].collect_score;
3534   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3535     GfxFrame[x][y] = ChangeDelay[x][y];
3536
3537   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3538     DrawLevelGraphicAnimation(x, y, graphic);
3539 }
3540 #endif
3541
3542 static void ResetGfxAnimation(int x, int y)
3543 {
3544   GfxAction[x][y] = ACTION_DEFAULT;
3545   GfxDir[x][y] = MovDir[x][y];
3546   GfxFrame[x][y] = 0;
3547
3548 #if USE_GFX_RESET_GFX_ANIMATION
3549   ResetGfxFrame(x, y, FALSE);
3550 #endif
3551 }
3552
3553 static void ResetRandomAnimationValue(int x, int y)
3554 {
3555   GfxRandom[x][y] = INIT_GFX_RANDOM();
3556 }
3557
3558 void InitMovingField(int x, int y, int direction)
3559 {
3560   int element = Feld[x][y];
3561   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3562   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3563   int newx = x + dx;
3564   int newy = y + dy;
3565   boolean is_moving_before, is_moving_after;
3566 #if 0
3567   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3568 #endif
3569
3570   /* check if element was/is moving or being moved before/after mode change */
3571 #if 1
3572 #if 1
3573   is_moving_before = (WasJustMoving[x][y] != 0);
3574 #else
3575   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3576   is_moving_before = WasJustMoving[x][y];
3577 #endif
3578 #else
3579   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3580 #endif
3581   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3582
3583   /* reset animation only for moving elements which change direction of moving
3584      or which just started or stopped moving
3585      (else CEs with property "can move" / "not moving" are reset each frame) */
3586 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3587 #if 1
3588   if (is_moving_before != is_moving_after ||
3589       direction != MovDir[x][y])
3590     ResetGfxAnimation(x, y);
3591 #else
3592   if ((is_moving_before || is_moving_after) && !continues_moving)
3593     ResetGfxAnimation(x, y);
3594 #endif
3595 #else
3596   if (!continues_moving)
3597     ResetGfxAnimation(x, y);
3598 #endif
3599
3600   MovDir[x][y] = direction;
3601   GfxDir[x][y] = direction;
3602
3603 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3604   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3605                      direction == MV_DOWN && CAN_FALL(element) ?
3606                      ACTION_FALLING : ACTION_MOVING);
3607 #else
3608   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3609                      ACTION_FALLING : ACTION_MOVING);
3610 #endif
3611
3612   /* this is needed for CEs with property "can move" / "not moving" */
3613
3614   if (is_moving_after)
3615   {
3616     if (Feld[newx][newy] == EL_EMPTY)
3617       Feld[newx][newy] = EL_BLOCKED;
3618
3619     MovDir[newx][newy] = MovDir[x][y];
3620
3621 #if USE_NEW_CUSTOM_VALUE
3622     CustomValue[newx][newy] = CustomValue[x][y];
3623 #endif
3624
3625     GfxFrame[newx][newy] = GfxFrame[x][y];
3626     GfxRandom[newx][newy] = GfxRandom[x][y];
3627     GfxAction[newx][newy] = GfxAction[x][y];
3628     GfxDir[newx][newy] = GfxDir[x][y];
3629   }
3630 }
3631
3632 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3633 {
3634   int direction = MovDir[x][y];
3635   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3636   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3637
3638   *goes_to_x = newx;
3639   *goes_to_y = newy;
3640 }
3641
3642 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3643 {
3644   int oldx = x, oldy = y;
3645   int direction = MovDir[x][y];
3646
3647   if (direction == MV_LEFT)
3648     oldx++;
3649   else if (direction == MV_RIGHT)
3650     oldx--;
3651   else if (direction == MV_UP)
3652     oldy++;
3653   else if (direction == MV_DOWN)
3654     oldy--;
3655
3656   *comes_from_x = oldx;
3657   *comes_from_y = oldy;
3658 }
3659
3660 int MovingOrBlocked2Element(int x, int y)
3661 {
3662   int element = Feld[x][y];
3663
3664   if (element == EL_BLOCKED)
3665   {
3666     int oldx, oldy;
3667
3668     Blocked2Moving(x, y, &oldx, &oldy);
3669     return Feld[oldx][oldy];
3670   }
3671   else
3672     return element;
3673 }
3674
3675 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3676 {
3677   /* like MovingOrBlocked2Element(), but if element is moving
3678      and (x,y) is the field the moving element is just leaving,
3679      return EL_BLOCKED instead of the element value */
3680   int element = Feld[x][y];
3681
3682   if (IS_MOVING(x, y))
3683   {
3684     if (element == EL_BLOCKED)
3685     {
3686       int oldx, oldy;
3687
3688       Blocked2Moving(x, y, &oldx, &oldy);
3689       return Feld[oldx][oldy];
3690     }
3691     else
3692       return EL_BLOCKED;
3693   }
3694   else
3695     return element;
3696 }
3697
3698 static void RemoveField(int x, int y)
3699 {
3700   Feld[x][y] = EL_EMPTY;
3701
3702   MovPos[x][y] = 0;
3703   MovDir[x][y] = 0;
3704   MovDelay[x][y] = 0;
3705
3706 #if USE_NEW_CUSTOM_VALUE
3707   CustomValue[x][y] = 0;
3708 #endif
3709
3710   AmoebaNr[x][y] = 0;
3711   ChangeDelay[x][y] = 0;
3712   ChangePage[x][y] = -1;
3713   Pushed[x][y] = FALSE;
3714
3715 #if 0
3716   ExplodeField[x][y] = EX_TYPE_NONE;
3717 #endif
3718
3719   GfxElement[x][y] = EL_UNDEFINED;
3720   GfxAction[x][y] = ACTION_DEFAULT;
3721   GfxDir[x][y] = MV_NONE;
3722 }
3723
3724 void RemoveMovingField(int x, int y)
3725 {
3726   int oldx = x, oldy = y, newx = x, newy = y;
3727   int element = Feld[x][y];
3728   int next_element = EL_UNDEFINED;
3729
3730   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3731     return;
3732
3733   if (IS_MOVING(x, y))
3734   {
3735     Moving2Blocked(x, y, &newx, &newy);
3736
3737     if (Feld[newx][newy] != EL_BLOCKED)
3738     {
3739       /* element is moving, but target field is not free (blocked), but
3740          already occupied by something different (example: acid pool);
3741          in this case, only remove the moving field, but not the target */
3742
3743       RemoveField(oldx, oldy);
3744
3745       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3746
3747       DrawLevelField(oldx, oldy);
3748
3749       return;
3750     }
3751   }
3752   else if (element == EL_BLOCKED)
3753   {
3754     Blocked2Moving(x, y, &oldx, &oldy);
3755     if (!IS_MOVING(oldx, oldy))
3756       return;
3757   }
3758
3759   if (element == EL_BLOCKED &&
3760       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3761        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3762        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3763        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3764        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3765        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3766     next_element = get_next_element(Feld[oldx][oldy]);
3767
3768   RemoveField(oldx, oldy);
3769   RemoveField(newx, newy);
3770
3771   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3772
3773   if (next_element != EL_UNDEFINED)
3774     Feld[oldx][oldy] = next_element;
3775
3776   DrawLevelField(oldx, oldy);
3777   DrawLevelField(newx, newy);
3778 }
3779
3780 void DrawDynamite(int x, int y)
3781 {
3782   int sx = SCREENX(x), sy = SCREENY(y);
3783   int graphic = el2img(Feld[x][y]);
3784   int frame;
3785
3786   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3787     return;
3788
3789   if (IS_WALKABLE_INSIDE(Back[x][y]))
3790     return;
3791
3792   if (Back[x][y])
3793     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3794   else if (Store[x][y])
3795     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3796
3797   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3798
3799   if (Back[x][y] || Store[x][y])
3800     DrawGraphicThruMask(sx, sy, graphic, frame);
3801   else
3802     DrawGraphic(sx, sy, graphic, frame);
3803 }
3804
3805 void CheckDynamite(int x, int y)
3806 {
3807   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3808   {
3809     MovDelay[x][y]--;
3810
3811     if (MovDelay[x][y] != 0)
3812     {
3813       DrawDynamite(x, y);
3814       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3815
3816       return;
3817     }
3818   }
3819
3820   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3821
3822   Bang(x, y);
3823 }
3824
3825 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3826 {
3827   boolean num_checked_players = 0;
3828   int i;
3829
3830   for (i = 0; i < MAX_PLAYERS; i++)
3831   {
3832     if (stored_player[i].active)
3833     {
3834       int sx = stored_player[i].jx;
3835       int sy = stored_player[i].jy;
3836
3837       if (num_checked_players == 0)
3838       {
3839         *sx1 = *sx2 = sx;
3840         *sy1 = *sy2 = sy;
3841       }
3842       else
3843       {
3844         *sx1 = MIN(*sx1, sx);
3845         *sy1 = MIN(*sy1, sy);
3846         *sx2 = MAX(*sx2, sx);
3847         *sy2 = MAX(*sy2, sy);
3848       }
3849
3850       num_checked_players++;
3851     }
3852   }
3853 }
3854
3855 static boolean checkIfAllPlayersFitToScreen_RND()
3856 {
3857   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3858
3859   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3860
3861   return (sx2 - sx1 < SCR_FIELDX &&
3862           sy2 - sy1 < SCR_FIELDY);
3863 }
3864
3865 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3866 {
3867   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3868
3869   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3870
3871   *sx = (sx1 + sx2) / 2;
3872   *sy = (sy1 + sy2) / 2;
3873 }
3874
3875 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3876                         boolean center_screen, boolean quick_relocation)
3877 {
3878   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3879   boolean no_delay = (tape.warp_forward);
3880   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3881   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3882
3883   if (quick_relocation)
3884   {
3885     int offset = (setup.scroll_delay ? 3 : 0);
3886
3887     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3888     {
3889       if (center_screen)
3890       {
3891         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3892                     x > SBX_Right + MIDPOSX ? SBX_Right :
3893                     x - MIDPOSX);
3894
3895         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3896                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3897                     y - MIDPOSY);
3898       }
3899       else
3900       {
3901         /* quick relocation (without scrolling), but do not center screen */
3902
3903         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3904                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3905                                old_x - MIDPOSX);
3906
3907         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3908                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3909                                old_y - MIDPOSY);
3910
3911         int offset_x = x + (scroll_x - center_scroll_x);
3912         int offset_y = y + (scroll_y - center_scroll_y);
3913
3914         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3915                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3916                     offset_x - MIDPOSX);
3917
3918         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3919                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3920                     offset_y - MIDPOSY);
3921       }
3922     }
3923     else
3924     {
3925       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3926           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3927         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3928
3929       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3930           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3931         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3932
3933       /* don't scroll over playfield boundaries */
3934       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3935         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3936
3937       /* don't scroll over playfield boundaries */
3938       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3939         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3940     }
3941
3942     RedrawPlayfield(TRUE, 0,0,0,0);
3943   }
3944   else
3945   {
3946     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3947                      x > SBX_Right + MIDPOSX ? SBX_Right :
3948                      x - MIDPOSX);
3949
3950     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3951                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3952                      y - MIDPOSY);
3953
3954     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3955
3956     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3957     {
3958       int dx = 0, dy = 0;
3959       int fx = FX, fy = FY;
3960
3961       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3962       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3963
3964       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3965         break;
3966
3967       scroll_x -= dx;
3968       scroll_y -= dy;
3969
3970       fx += dx * TILEX / 2;
3971       fy += dy * TILEY / 2;
3972
3973       ScrollLevel(dx, dy);
3974       DrawAllPlayers();
3975
3976       /* scroll in two steps of half tile size to make things smoother */
3977       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3978       FlushDisplay();
3979       Delay(wait_delay_value);
3980
3981       /* scroll second step to align at full tile size */
3982       BackToFront();
3983       Delay(wait_delay_value);
3984     }
3985
3986     DrawAllPlayers();
3987     BackToFront();
3988     Delay(wait_delay_value);
3989   }
3990 }
3991
3992 void RelocatePlayer(int jx, int jy, int el_player_raw)
3993 {
3994   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3995   int player_nr = GET_PLAYER_NR(el_player);
3996   struct PlayerInfo *player = &stored_player[player_nr];
3997   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3998   boolean no_delay = (tape.warp_forward);
3999   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4000   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4001   int old_jx = player->jx;
4002   int old_jy = player->jy;
4003   int old_element = Feld[old_jx][old_jy];
4004   int element = Feld[jx][jy];
4005   boolean player_relocated = (old_jx != jx || old_jy != jy);
4006
4007   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4008   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4009   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4010   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4011   int leave_side_horiz = move_dir_horiz;
4012   int leave_side_vert  = move_dir_vert;
4013   int enter_side = enter_side_horiz | enter_side_vert;
4014   int leave_side = leave_side_horiz | leave_side_vert;
4015
4016   if (player->GameOver)         /* do not reanimate dead player */
4017     return;
4018
4019   if (!player_relocated)        /* no need to relocate the player */
4020     return;
4021
4022   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4023   {
4024     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4025     DrawLevelField(jx, jy);
4026   }
4027
4028   if (player->present)
4029   {
4030     while (player->MovPos)
4031     {
4032       ScrollPlayer(player, SCROLL_GO_ON);
4033       ScrollScreen(NULL, SCROLL_GO_ON);
4034
4035       AdvanceFrameAndPlayerCounters(player->index_nr);
4036
4037       DrawPlayer(player);
4038
4039       BackToFront();
4040       Delay(wait_delay_value);
4041     }
4042
4043     DrawPlayer(player);         /* needed here only to cleanup last field */
4044     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4045
4046     player->is_moving = FALSE;
4047   }
4048
4049   if (IS_CUSTOM_ELEMENT(old_element))
4050     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4051                                CE_LEFT_BY_PLAYER,
4052                                player->index_bit, leave_side);
4053
4054   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4055                                       CE_PLAYER_LEAVES_X,
4056                                       player->index_bit, leave_side);
4057
4058   Feld[jx][jy] = el_player;
4059   InitPlayerField(jx, jy, el_player, TRUE);
4060
4061   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4062   {
4063     Feld[jx][jy] = element;
4064     InitField(jx, jy, FALSE);
4065   }
4066
4067   /* only visually relocate centered player */
4068   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4069                      FALSE, level.instant_relocation);
4070
4071   TestIfPlayerTouchesBadThing(jx, jy);
4072   TestIfPlayerTouchesCustomElement(jx, jy);
4073
4074   if (IS_CUSTOM_ELEMENT(element))
4075     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4076                                player->index_bit, enter_side);
4077
4078   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4079                                       player->index_bit, enter_side);
4080 }
4081
4082 void Explode(int ex, int ey, int phase, int mode)
4083 {
4084   int x, y;
4085   int last_phase;
4086   int border_element;
4087
4088   /* !!! eliminate this variable !!! */
4089   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4090
4091   if (game.explosions_delayed)
4092   {
4093     ExplodeField[ex][ey] = mode;
4094     return;
4095   }
4096
4097   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4098   {
4099     int center_element = Feld[ex][ey];
4100     int artwork_element, explosion_element;     /* set these values later */
4101
4102 #if 0
4103     /* --- This is only really needed (and now handled) in "Impact()". --- */
4104     /* do not explode moving elements that left the explode field in time */
4105     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4106         center_element == EL_EMPTY &&
4107         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4108       return;
4109 #endif
4110
4111 #if 0
4112     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4113     if (mode == EX_TYPE_NORMAL ||
4114         mode == EX_TYPE_CENTER ||
4115         mode == EX_TYPE_CROSS)
4116       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4117 #endif
4118
4119     /* remove things displayed in background while burning dynamite */
4120     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4121       Back[ex][ey] = 0;
4122
4123     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4124     {
4125       /* put moving element to center field (and let it explode there) */
4126       center_element = MovingOrBlocked2Element(ex, ey);
4127       RemoveMovingField(ex, ey);
4128       Feld[ex][ey] = center_element;
4129     }
4130
4131     /* now "center_element" is finally determined -- set related values now */
4132     artwork_element = center_element;           /* for custom player artwork */
4133     explosion_element = center_element;         /* for custom player artwork */
4134
4135     if (IS_PLAYER(ex, ey))
4136     {
4137       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4138
4139       artwork_element = stored_player[player_nr].artwork_element;
4140
4141       if (level.use_explosion_element[player_nr])
4142       {
4143         explosion_element = level.explosion_element[player_nr];
4144         artwork_element = explosion_element;
4145       }
4146     }
4147
4148 #if 1
4149     if (mode == EX_TYPE_NORMAL ||
4150         mode == EX_TYPE_CENTER ||
4151         mode == EX_TYPE_CROSS)
4152       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4153 #endif
4154
4155     last_phase = element_info[explosion_element].explosion_delay + 1;
4156
4157     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4158     {
4159       int xx = x - ex + 1;
4160       int yy = y - ey + 1;
4161       int element;
4162
4163       if (!IN_LEV_FIELD(x, y) ||
4164           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4165           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4166         continue;
4167
4168       element = Feld[x][y];
4169
4170       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4171       {
4172         element = MovingOrBlocked2Element(x, y);
4173
4174         if (!IS_EXPLOSION_PROOF(element))
4175           RemoveMovingField(x, y);
4176       }
4177
4178       /* indestructible elements can only explode in center (but not flames) */
4179       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4180                                            mode == EX_TYPE_BORDER)) ||
4181           element == EL_FLAMES)
4182         continue;
4183
4184       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4185          behaviour, for example when touching a yamyam that explodes to rocks
4186          with active deadly shield, a rock is created under the player !!! */
4187       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4188 #if 0
4189       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4190           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4191            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4192 #else
4193       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4194 #endif
4195       {
4196         if (IS_ACTIVE_BOMB(element))
4197         {
4198           /* re-activate things under the bomb like gate or penguin */
4199           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4200           Back[x][y] = 0;
4201         }
4202
4203         continue;
4204       }
4205
4206       /* save walkable background elements while explosion on same tile */
4207       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4208           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4209         Back[x][y] = element;
4210
4211       /* ignite explodable elements reached by other explosion */
4212       if (element == EL_EXPLOSION)
4213         element = Store2[x][y];
4214
4215       if (AmoebaNr[x][y] &&
4216           (element == EL_AMOEBA_FULL ||
4217            element == EL_BD_AMOEBA ||
4218            element == EL_AMOEBA_GROWING))
4219       {
4220         AmoebaCnt[AmoebaNr[x][y]]--;
4221         AmoebaCnt2[AmoebaNr[x][y]]--;
4222       }
4223
4224       RemoveField(x, y);
4225
4226       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4227       {
4228         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4229
4230         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4231
4232         if (PLAYERINFO(ex, ey)->use_murphy)
4233           Store[x][y] = EL_EMPTY;
4234       }
4235
4236       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4237          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4238       else if (ELEM_IS_PLAYER(center_element))
4239         Store[x][y] = EL_EMPTY;
4240       else if (center_element == EL_YAMYAM)
4241         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4242       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4243         Store[x][y] = element_info[center_element].content.e[xx][yy];
4244 #if 1
4245       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4246          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4247          otherwise) -- FIX THIS !!! */
4248       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4249         Store[x][y] = element_info[element].content.e[1][1];
4250 #else
4251       else if (!CAN_EXPLODE(element))
4252         Store[x][y] = element_info[element].content.e[1][1];
4253 #endif
4254       else
4255         Store[x][y] = EL_EMPTY;
4256
4257       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4258           center_element == EL_AMOEBA_TO_DIAMOND)
4259         Store2[x][y] = element;
4260
4261       Feld[x][y] = EL_EXPLOSION;
4262       GfxElement[x][y] = artwork_element;
4263
4264       ExplodePhase[x][y] = 1;
4265       ExplodeDelay[x][y] = last_phase;
4266
4267       Stop[x][y] = TRUE;
4268     }
4269
4270     if (center_element == EL_YAMYAM)
4271       game.yamyam_content_nr =
4272         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4273
4274     return;
4275   }
4276
4277   if (Stop[ex][ey])
4278     return;
4279
4280   x = ex;
4281   y = ey;
4282
4283   if (phase == 1)
4284     GfxFrame[x][y] = 0;         /* restart explosion animation */
4285
4286   last_phase = ExplodeDelay[x][y];
4287
4288   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4289
4290 #ifdef DEBUG
4291
4292   /* activate this even in non-DEBUG version until cause for crash in
4293      getGraphicAnimationFrame() (see below) is found and eliminated */
4294
4295 #endif
4296 #if 1
4297
4298 #if 1
4299   /* this can happen if the player leaves an explosion just in time */
4300   if (GfxElement[x][y] == EL_UNDEFINED)
4301     GfxElement[x][y] = EL_EMPTY;
4302 #else
4303   if (GfxElement[x][y] == EL_UNDEFINED)
4304   {
4305     printf("\n\n");
4306     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4307     printf("Explode(): This should never happen!\n");
4308     printf("\n\n");
4309
4310     GfxElement[x][y] = EL_EMPTY;
4311   }
4312 #endif
4313
4314 #endif
4315
4316   border_element = Store2[x][y];
4317   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4318     border_element = StorePlayer[x][y];
4319
4320   if (phase == element_info[border_element].ignition_delay ||
4321       phase == last_phase)
4322   {
4323     boolean border_explosion = FALSE;
4324
4325     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4326         !PLAYER_EXPLOSION_PROTECTED(x, y))
4327     {
4328       KillPlayerUnlessExplosionProtected(x, y);
4329       border_explosion = TRUE;
4330     }
4331     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4332     {
4333       Feld[x][y] = Store2[x][y];
4334       Store2[x][y] = 0;
4335       Bang(x, y);
4336       border_explosion = TRUE;
4337     }
4338     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4339     {
4340       AmoebeUmwandeln(x, y);
4341       Store2[x][y] = 0;
4342       border_explosion = TRUE;
4343     }
4344
4345     /* if an element just explodes due to another explosion (chain-reaction),
4346        do not immediately end the new explosion when it was the last frame of
4347        the explosion (as it would be done in the following "if"-statement!) */
4348     if (border_explosion && phase == last_phase)
4349       return;
4350   }
4351
4352   if (phase == last_phase)
4353   {
4354     int element;
4355
4356     element = Feld[x][y] = Store[x][y];
4357     Store[x][y] = Store2[x][y] = 0;
4358     GfxElement[x][y] = EL_UNDEFINED;
4359
4360     /* player can escape from explosions and might therefore be still alive */
4361     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4362         element <= EL_PLAYER_IS_EXPLODING_4)
4363     {
4364       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4365       int explosion_element = EL_PLAYER_1 + player_nr;
4366       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4367       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4368
4369       if (level.use_explosion_element[player_nr])
4370         explosion_element = level.explosion_element[player_nr];
4371
4372       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4373                     element_info[explosion_element].content.e[xx][yy]);
4374     }
4375
4376     /* restore probably existing indestructible background element */
4377     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4378       element = Feld[x][y] = Back[x][y];
4379     Back[x][y] = 0;
4380
4381     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4382     GfxDir[x][y] = MV_NONE;
4383     ChangeDelay[x][y] = 0;
4384     ChangePage[x][y] = -1;
4385
4386 #if USE_NEW_CUSTOM_VALUE
4387     CustomValue[x][y] = 0;
4388 #endif
4389
4390     InitField_WithBug2(x, y, FALSE);
4391
4392     DrawLevelField(x, y);
4393
4394     TestIfElementTouchesCustomElement(x, y);
4395
4396     if (GFX_CRUMBLED(element))
4397       DrawLevelFieldCrumbledSandNeighbours(x, y);
4398
4399     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4400       StorePlayer[x][y] = 0;
4401
4402     if (ELEM_IS_PLAYER(element))
4403       RelocatePlayer(x, y, element);
4404   }
4405   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4406   {
4407     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4408     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4409
4410     if (phase == delay)
4411       DrawLevelFieldCrumbledSand(x, y);
4412
4413     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4414     {
4415       DrawLevelElement(x, y, Back[x][y]);
4416       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4417     }
4418     else if (IS_WALKABLE_UNDER(Back[x][y]))
4419     {
4420       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4421       DrawLevelElementThruMask(x, y, Back[x][y]);
4422     }
4423     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4424       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4425   }
4426 }
4427
4428 void DynaExplode(int ex, int ey)
4429 {
4430   int i, j;
4431   int dynabomb_element = Feld[ex][ey];
4432   int dynabomb_size = 1;
4433   boolean dynabomb_xl = FALSE;
4434   struct PlayerInfo *player;
4435   static int xy[4][2] =
4436   {
4437     { 0, -1 },
4438     { -1, 0 },
4439     { +1, 0 },
4440     { 0, +1 }
4441   };
4442
4443   if (IS_ACTIVE_BOMB(dynabomb_element))
4444   {
4445     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4446     dynabomb_size = player->dynabomb_size;
4447     dynabomb_xl = player->dynabomb_xl;
4448     player->dynabombs_left++;
4449   }
4450
4451   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4452
4453   for (i = 0; i < NUM_DIRECTIONS; i++)
4454   {
4455     for (j = 1; j <= dynabomb_size; j++)
4456     {
4457       int x = ex + j * xy[i][0];
4458       int y = ey + j * xy[i][1];
4459       int element;
4460
4461       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4462         break;
4463
4464       element = Feld[x][y];
4465
4466       /* do not restart explosions of fields with active bombs */
4467       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4468         continue;
4469
4470       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4471
4472       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4473           !IS_DIGGABLE(element) && !dynabomb_xl)
4474         break;
4475     }
4476   }
4477 }
4478
4479 void Bang(int x, int y)
4480 {
4481   int element = MovingOrBlocked2Element(x, y);
4482   int explosion_type = EX_TYPE_NORMAL;
4483
4484   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4485   {
4486     struct PlayerInfo *player = PLAYERINFO(x, y);
4487
4488     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4489                             player->element_nr);
4490
4491     if (level.use_explosion_element[player->index_nr])
4492     {
4493       int explosion_element = level.explosion_element[player->index_nr];
4494
4495       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4496         explosion_type = EX_TYPE_CROSS;
4497       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4498         explosion_type = EX_TYPE_CENTER;
4499     }
4500   }
4501
4502   switch (element)
4503   {
4504     case EL_BUG:
4505     case EL_SPACESHIP:
4506     case EL_BD_BUTTERFLY:
4507     case EL_BD_FIREFLY:
4508     case EL_YAMYAM:
4509     case EL_DARK_YAMYAM:
4510     case EL_ROBOT:
4511     case EL_PACMAN:
4512     case EL_MOLE:
4513       RaiseScoreElement(element);
4514       break;
4515
4516     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4517     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4518     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4519     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4520     case EL_DYNABOMB_INCREASE_NUMBER:
4521     case EL_DYNABOMB_INCREASE_SIZE:
4522     case EL_DYNABOMB_INCREASE_POWER:
4523       explosion_type = EX_TYPE_DYNA;
4524       break;
4525
4526     case EL_DC_LANDMINE:
4527 #if 0
4528     case EL_EM_EXIT_OPEN:
4529     case EL_EM_STEEL_EXIT_OPEN:
4530 #endif
4531       explosion_type = EX_TYPE_CENTER;
4532       break;
4533
4534     case EL_PENGUIN:
4535     case EL_LAMP:
4536     case EL_LAMP_ACTIVE:
4537     case EL_AMOEBA_TO_DIAMOND:
4538       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4539         explosion_type = EX_TYPE_CENTER;
4540       break;
4541
4542     default:
4543       if (element_info[element].explosion_type == EXPLODES_CROSS)
4544         explosion_type = EX_TYPE_CROSS;
4545       else if (element_info[element].explosion_type == EXPLODES_1X1)
4546         explosion_type = EX_TYPE_CENTER;
4547       break;
4548   }
4549
4550   if (explosion_type == EX_TYPE_DYNA)
4551     DynaExplode(x, y);
4552   else
4553     Explode(x, y, EX_PHASE_START, explosion_type);
4554
4555   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4556 }
4557
4558 void SplashAcid(int x, int y)
4559 {
4560   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4561       (!IN_LEV_FIELD(x - 1, y - 2) ||
4562        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4563     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4564
4565   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4566       (!IN_LEV_FIELD(x + 1, y - 2) ||
4567        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4568     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4569
4570   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4571 }
4572
4573 static void InitBeltMovement()
4574 {
4575   static int belt_base_element[4] =
4576   {
4577     EL_CONVEYOR_BELT_1_LEFT,
4578     EL_CONVEYOR_BELT_2_LEFT,
4579     EL_CONVEYOR_BELT_3_LEFT,
4580     EL_CONVEYOR_BELT_4_LEFT
4581   };
4582   static int belt_base_active_element[4] =
4583   {
4584     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4585     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4586     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4587     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4588   };
4589
4590   int x, y, i, j;
4591
4592   /* set frame order for belt animation graphic according to belt direction */
4593   for (i = 0; i < NUM_BELTS; i++)
4594   {
4595     int belt_nr = i;
4596
4597     for (j = 0; j < NUM_BELT_PARTS; j++)
4598     {
4599       int element = belt_base_active_element[belt_nr] + j;
4600       int graphic = el2img(element);
4601
4602       if (game.belt_dir[i] == MV_LEFT)
4603         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4604       else
4605         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4606     }
4607   }
4608
4609   SCAN_PLAYFIELD(x, y)
4610   {
4611     int element = Feld[x][y];
4612
4613     for (i = 0; i < NUM_BELTS; i++)
4614     {
4615       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4616       {
4617         int e_belt_nr = getBeltNrFromBeltElement(element);
4618         int belt_nr = i;
4619
4620         if (e_belt_nr == belt_nr)
4621         {
4622           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4623
4624           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4625         }
4626       }
4627     }
4628   }
4629 }
4630
4631 static void ToggleBeltSwitch(int x, int y)
4632 {
4633   static int belt_base_element[4] =
4634   {
4635     EL_CONVEYOR_BELT_1_LEFT,
4636     EL_CONVEYOR_BELT_2_LEFT,
4637     EL_CONVEYOR_BELT_3_LEFT,
4638     EL_CONVEYOR_BELT_4_LEFT
4639   };
4640   static int belt_base_active_element[4] =
4641   {
4642     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4643     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4644     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4645     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4646   };
4647   static int belt_base_switch_element[4] =
4648   {
4649     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4650     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4651     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4652     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4653   };
4654   static int belt_move_dir[4] =
4655   {
4656     MV_LEFT,
4657     MV_NONE,
4658     MV_RIGHT,
4659     MV_NONE,
4660   };
4661
4662   int element = Feld[x][y];
4663   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4664   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4665   int belt_dir = belt_move_dir[belt_dir_nr];
4666   int xx, yy, i;
4667
4668   if (!IS_BELT_SWITCH(element))
4669     return;
4670
4671   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4672   game.belt_dir[belt_nr] = belt_dir;
4673
4674   if (belt_dir_nr == 3)
4675     belt_dir_nr = 1;
4676
4677   /* set frame order for belt animation graphic according to belt direction */
4678   for (i = 0; i < NUM_BELT_PARTS; i++)
4679   {
4680     int element = belt_base_active_element[belt_nr] + i;
4681     int graphic = el2img(element);
4682
4683     if (belt_dir == MV_LEFT)
4684       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4685     else
4686       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4687   }
4688
4689   SCAN_PLAYFIELD(xx, yy)
4690   {
4691     int element = Feld[xx][yy];
4692
4693     if (IS_BELT_SWITCH(element))
4694     {
4695       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4696
4697       if (e_belt_nr == belt_nr)
4698       {
4699         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4700         DrawLevelField(xx, yy);
4701       }
4702     }
4703     else if (IS_BELT(element) && belt_dir != MV_NONE)
4704     {
4705       int e_belt_nr = getBeltNrFromBeltElement(element);
4706
4707       if (e_belt_nr == belt_nr)
4708       {
4709         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4710
4711         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4712         DrawLevelField(xx, yy);
4713       }
4714     }
4715     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4716     {
4717       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4718
4719       if (e_belt_nr == belt_nr)
4720       {
4721         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4722
4723         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4724         DrawLevelField(xx, yy);
4725       }
4726     }
4727   }
4728 }
4729
4730 static void ToggleSwitchgateSwitch(int x, int y)
4731 {
4732   int xx, yy;
4733
4734   game.switchgate_pos = !game.switchgate_pos;
4735
4736   SCAN_PLAYFIELD(xx, yy)
4737   {
4738     int element = Feld[xx][yy];
4739
4740 #if !USE_BOTH_SWITCHGATE_SWITCHES
4741     if (element == EL_SWITCHGATE_SWITCH_UP ||
4742         element == EL_SWITCHGATE_SWITCH_DOWN)
4743     {
4744       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4745       DrawLevelField(xx, yy);
4746     }
4747     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4748              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4749     {
4750       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4751       DrawLevelField(xx, yy);
4752     }
4753 #else
4754     if (element == EL_SWITCHGATE_SWITCH_UP)
4755     {
4756       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4757       DrawLevelField(xx, yy);
4758     }
4759     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4760     {
4761       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4762       DrawLevelField(xx, yy);
4763     }
4764     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4765     {
4766       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4767       DrawLevelField(xx, yy);
4768     }
4769     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4770     {
4771       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4772       DrawLevelField(xx, yy);
4773     }
4774 #endif
4775     else if (element == EL_SWITCHGATE_OPEN ||
4776              element == EL_SWITCHGATE_OPENING)
4777     {
4778       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4779
4780       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4781     }
4782     else if (element == EL_SWITCHGATE_CLOSED ||
4783              element == EL_SWITCHGATE_CLOSING)
4784     {
4785       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4786
4787       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4788     }
4789   }
4790 }
4791
4792 static int getInvisibleActiveFromInvisibleElement(int element)
4793 {
4794   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4795           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4796           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4797           element);
4798 }
4799
4800 static int getInvisibleFromInvisibleActiveElement(int element)
4801 {
4802   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4803           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4804           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4805           element);
4806 }
4807
4808 static void RedrawAllLightSwitchesAndInvisibleElements()
4809 {
4810   int x, y;
4811
4812   SCAN_PLAYFIELD(x, y)
4813   {
4814     int element = Feld[x][y];
4815
4816     if (element == EL_LIGHT_SWITCH &&
4817         game.light_time_left > 0)
4818     {
4819       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4820       DrawLevelField(x, y);
4821     }
4822     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4823              game.light_time_left == 0)
4824     {
4825       Feld[x][y] = EL_LIGHT_SWITCH;
4826       DrawLevelField(x, y);
4827     }
4828     else if (element == EL_EMC_DRIPPER &&
4829              game.light_time_left > 0)
4830     {
4831       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4832       DrawLevelField(x, y);
4833     }
4834     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4835              game.light_time_left == 0)
4836     {
4837       Feld[x][y] = EL_EMC_DRIPPER;
4838       DrawLevelField(x, y);
4839     }
4840     else if (element == EL_INVISIBLE_STEELWALL ||
4841              element == EL_INVISIBLE_WALL ||
4842              element == EL_INVISIBLE_SAND)
4843     {
4844       if (game.light_time_left > 0)
4845         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4846
4847       DrawLevelField(x, y);
4848
4849       /* uncrumble neighbour fields, if needed */
4850       if (element == EL_INVISIBLE_SAND)
4851         DrawLevelFieldCrumbledSandNeighbours(x, y);
4852     }
4853     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4854              element == EL_INVISIBLE_WALL_ACTIVE ||
4855              element == EL_INVISIBLE_SAND_ACTIVE)
4856     {
4857       if (game.light_time_left == 0)
4858         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4859
4860       DrawLevelField(x, y);
4861
4862       /* re-crumble neighbour fields, if needed */
4863       if (element == EL_INVISIBLE_SAND)
4864         DrawLevelFieldCrumbledSandNeighbours(x, y);
4865     }
4866   }
4867 }
4868
4869 static void RedrawAllInvisibleElementsForLenses()
4870 {
4871   int x, y;
4872
4873   SCAN_PLAYFIELD(x, y)
4874   {
4875     int element = Feld[x][y];
4876
4877     if (element == EL_EMC_DRIPPER &&
4878         game.lenses_time_left > 0)
4879     {
4880       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4881       DrawLevelField(x, y);
4882     }
4883     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4884              game.lenses_time_left == 0)
4885     {
4886       Feld[x][y] = EL_EMC_DRIPPER;
4887       DrawLevelField(x, y);
4888     }
4889     else if (element == EL_INVISIBLE_STEELWALL ||
4890              element == EL_INVISIBLE_WALL ||
4891              element == EL_INVISIBLE_SAND)
4892     {
4893       if (game.lenses_time_left > 0)
4894         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4895
4896       DrawLevelField(x, y);
4897
4898       /* uncrumble neighbour fields, if needed */
4899       if (element == EL_INVISIBLE_SAND)
4900         DrawLevelFieldCrumbledSandNeighbours(x, y);
4901     }
4902     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4903              element == EL_INVISIBLE_WALL_ACTIVE ||
4904              element == EL_INVISIBLE_SAND_ACTIVE)
4905     {
4906       if (game.lenses_time_left == 0)
4907         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4908
4909       DrawLevelField(x, y);
4910
4911       /* re-crumble neighbour fields, if needed */
4912       if (element == EL_INVISIBLE_SAND)
4913         DrawLevelFieldCrumbledSandNeighbours(x, y);
4914     }
4915   }
4916 }
4917
4918 static void RedrawAllInvisibleElementsForMagnifier()
4919 {
4920   int x, y;
4921
4922   SCAN_PLAYFIELD(x, y)
4923   {
4924     int element = Feld[x][y];
4925
4926     if (element == EL_EMC_FAKE_GRASS &&
4927         game.magnify_time_left > 0)
4928     {
4929       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4930       DrawLevelField(x, y);
4931     }
4932     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4933              game.magnify_time_left == 0)
4934     {
4935       Feld[x][y] = EL_EMC_FAKE_GRASS;
4936       DrawLevelField(x, y);
4937     }
4938     else if (IS_GATE_GRAY(element) &&
4939              game.magnify_time_left > 0)
4940     {
4941       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4942                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4943                     IS_EM_GATE_GRAY(element) ?
4944                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4945                     IS_EMC_GATE_GRAY(element) ?
4946                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4947                     element);
4948       DrawLevelField(x, y);
4949     }
4950     else if (IS_GATE_GRAY_ACTIVE(element) &&
4951              game.magnify_time_left == 0)
4952     {
4953       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4954                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4955                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4956                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4957                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4958                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4959                     element);
4960       DrawLevelField(x, y);
4961     }
4962   }
4963 }
4964
4965 static void ToggleLightSwitch(int x, int y)
4966 {
4967   int element = Feld[x][y];
4968
4969   game.light_time_left =
4970     (element == EL_LIGHT_SWITCH ?
4971      level.time_light * FRAMES_PER_SECOND : 0);
4972
4973   RedrawAllLightSwitchesAndInvisibleElements();
4974 }
4975
4976 static void ActivateTimegateSwitch(int x, int y)
4977 {
4978   int xx, yy;
4979
4980   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4981
4982   SCAN_PLAYFIELD(xx, yy)
4983   {
4984     int element = Feld[xx][yy];
4985
4986     if (element == EL_TIMEGATE_CLOSED ||
4987         element == EL_TIMEGATE_CLOSING)
4988     {
4989       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4990       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4991     }
4992
4993     /*
4994     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4995     {
4996       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4997       DrawLevelField(xx, yy);
4998     }
4999     */
5000
5001   }
5002
5003 #if 1
5004   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5005                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5006 #else
5007   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5008 #endif
5009 }
5010
5011 void Impact(int x, int y)
5012 {
5013   boolean last_line = (y == lev_fieldy - 1);
5014   boolean object_hit = FALSE;
5015   boolean impact = (last_line || object_hit);
5016   int element = Feld[x][y];
5017   int smashed = EL_STEELWALL;
5018
5019   if (!last_line)       /* check if element below was hit */
5020   {
5021     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5022       return;
5023
5024     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5025                                          MovDir[x][y + 1] != MV_DOWN ||
5026                                          MovPos[x][y + 1] <= TILEY / 2));
5027
5028     /* do not smash moving elements that left the smashed field in time */
5029     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5030         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5031       object_hit = FALSE;
5032
5033 #if USE_QUICKSAND_IMPACT_BUGFIX
5034     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5035     {
5036       RemoveMovingField(x, y + 1);
5037       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5038       Feld[x][y + 2] = EL_ROCK;
5039       DrawLevelField(x, y + 2);
5040
5041       object_hit = TRUE;
5042     }
5043
5044     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5045     {
5046       RemoveMovingField(x, y + 1);
5047       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5048       Feld[x][y + 2] = EL_ROCK;
5049       DrawLevelField(x, y + 2);
5050
5051       object_hit = TRUE;
5052     }
5053 #endif
5054
5055     if (object_hit)
5056       smashed = MovingOrBlocked2Element(x, y + 1);
5057
5058     impact = (last_line || object_hit);
5059   }
5060
5061   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5062   {
5063     SplashAcid(x, y + 1);
5064     return;
5065   }
5066
5067   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5068   /* only reset graphic animation if graphic really changes after impact */
5069   if (impact &&
5070       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5071   {
5072     ResetGfxAnimation(x, y);
5073     DrawLevelField(x, y);
5074   }
5075
5076   if (impact && CAN_EXPLODE_IMPACT(element))
5077   {
5078     Bang(x, y);
5079     return;
5080   }
5081   else if (impact && element == EL_PEARL &&
5082            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5083   {
5084     ResetGfxAnimation(x, y);
5085
5086     Feld[x][y] = EL_PEARL_BREAKING;
5087     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5088     return;
5089   }
5090   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5091   {
5092     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5093
5094     return;
5095   }
5096
5097   if (impact && element == EL_AMOEBA_DROP)
5098   {
5099     if (object_hit && IS_PLAYER(x, y + 1))
5100       KillPlayerUnlessEnemyProtected(x, y + 1);
5101     else if (object_hit && smashed == EL_PENGUIN)
5102       Bang(x, y + 1);
5103     else
5104     {
5105       Feld[x][y] = EL_AMOEBA_GROWING;
5106       Store[x][y] = EL_AMOEBA_WET;
5107
5108       ResetRandomAnimationValue(x, y);
5109     }
5110     return;
5111   }
5112
5113   if (object_hit)               /* check which object was hit */
5114   {
5115     if ((CAN_PASS_MAGIC_WALL(element) && 
5116          (smashed == EL_MAGIC_WALL ||
5117           smashed == EL_BD_MAGIC_WALL)) ||
5118         (CAN_PASS_DC_MAGIC_WALL(element) &&
5119          smashed == EL_DC_MAGIC_WALL))
5120     {
5121       int xx, yy;
5122       int activated_magic_wall =
5123         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5124          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5125          EL_DC_MAGIC_WALL_ACTIVE);
5126
5127       /* activate magic wall / mill */
5128       SCAN_PLAYFIELD(xx, yy)
5129       {
5130         if (Feld[xx][yy] == smashed)
5131           Feld[xx][yy] = activated_magic_wall;
5132       }
5133
5134       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5135       game.magic_wall_active = TRUE;
5136
5137       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5138                             SND_MAGIC_WALL_ACTIVATING :
5139                             smashed == EL_BD_MAGIC_WALL ?
5140                             SND_BD_MAGIC_WALL_ACTIVATING :
5141                             SND_DC_MAGIC_WALL_ACTIVATING));
5142     }
5143
5144     if (IS_PLAYER(x, y + 1))
5145     {
5146       if (CAN_SMASH_PLAYER(element))
5147       {
5148         KillPlayerUnlessEnemyProtected(x, y + 1);
5149         return;
5150       }
5151     }
5152     else if (smashed == EL_PENGUIN)
5153     {
5154       if (CAN_SMASH_PLAYER(element))
5155       {
5156         Bang(x, y + 1);
5157         return;
5158       }
5159     }
5160     else if (element == EL_BD_DIAMOND)
5161     {
5162       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5163       {
5164         Bang(x, y + 1);
5165         return;
5166       }
5167     }
5168     else if (((element == EL_SP_INFOTRON ||
5169                element == EL_SP_ZONK) &&
5170               (smashed == EL_SP_SNIKSNAK ||
5171                smashed == EL_SP_ELECTRON ||
5172                smashed == EL_SP_DISK_ORANGE)) ||
5173              (element == EL_SP_INFOTRON &&
5174               smashed == EL_SP_DISK_YELLOW))
5175     {
5176       Bang(x, y + 1);
5177       return;
5178     }
5179     else if (CAN_SMASH_EVERYTHING(element))
5180     {
5181       if (IS_CLASSIC_ENEMY(smashed) ||
5182           CAN_EXPLODE_SMASHED(smashed))
5183       {
5184         Bang(x, y + 1);
5185         return;
5186       }
5187       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5188       {
5189         if (smashed == EL_LAMP ||
5190             smashed == EL_LAMP_ACTIVE)
5191         {
5192           Bang(x, y + 1);
5193           return;
5194         }
5195         else if (smashed == EL_NUT)
5196         {
5197           Feld[x][y + 1] = EL_NUT_BREAKING;
5198           PlayLevelSound(x, y, SND_NUT_BREAKING);
5199           RaiseScoreElement(EL_NUT);
5200           return;
5201         }
5202         else if (smashed == EL_PEARL)
5203         {
5204           ResetGfxAnimation(x, y);
5205
5206           Feld[x][y + 1] = EL_PEARL_BREAKING;
5207           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5208           return;
5209         }
5210         else if (smashed == EL_DIAMOND)
5211         {
5212           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5213           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5214           return;
5215         }
5216         else if (IS_BELT_SWITCH(smashed))
5217         {
5218           ToggleBeltSwitch(x, y + 1);
5219         }
5220         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5221                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5222                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5223                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5224         {
5225           ToggleSwitchgateSwitch(x, y + 1);
5226         }
5227         else if (smashed == EL_LIGHT_SWITCH ||
5228                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5229         {
5230           ToggleLightSwitch(x, y + 1);
5231         }
5232         else
5233         {
5234 #if 0
5235           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5236 #endif
5237
5238           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5239
5240           CheckElementChangeBySide(x, y + 1, smashed, element,
5241                                    CE_SWITCHED, CH_SIDE_TOP);
5242           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5243                                             CH_SIDE_TOP);
5244         }
5245       }
5246       else
5247       {
5248         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5249       }
5250     }
5251   }
5252
5253   /* play sound of magic wall / mill */
5254   if (!last_line &&
5255       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5256        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5257        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5258   {
5259     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5260       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5261     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5262       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5263     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5264       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5265
5266     return;
5267   }
5268
5269   /* play sound of object that hits the ground */
5270   if (last_line || object_hit)
5271     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5272 }
5273
5274 inline static void TurnRoundExt(int x, int y)
5275 {
5276   static struct
5277   {
5278     int dx, dy;
5279   } move_xy[] =
5280   {
5281     {  0,  0 },
5282     { -1,  0 },
5283     { +1,  0 },
5284     {  0,  0 },
5285     {  0, -1 },
5286     {  0,  0 }, { 0, 0 }, { 0, 0 },
5287     {  0, +1 }
5288   };
5289   static struct
5290   {
5291     int left, right, back;
5292   } turn[] =
5293   {
5294     { 0,        0,              0        },
5295     { MV_DOWN,  MV_UP,          MV_RIGHT },
5296     { MV_UP,    MV_DOWN,        MV_LEFT  },
5297     { 0,        0,              0        },
5298     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5299     { 0,        0,              0        },
5300     { 0,        0,              0        },
5301     { 0,        0,              0        },
5302     { MV_RIGHT, MV_LEFT,        MV_UP    }
5303   };
5304
5305   int element = Feld[x][y];
5306   int move_pattern = element_info[element].move_pattern;
5307
5308   int old_move_dir = MovDir[x][y];
5309   int left_dir  = turn[old_move_dir].left;
5310   int right_dir = turn[old_move_dir].right;
5311   int back_dir  = turn[old_move_dir].back;
5312
5313   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5314   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5315   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5316   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5317
5318   int left_x  = x + left_dx,  left_y  = y + left_dy;
5319   int right_x = x + right_dx, right_y = y + right_dy;
5320   int move_x  = x + move_dx,  move_y  = y + move_dy;
5321
5322   int xx, yy;
5323
5324   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5325   {
5326     TestIfBadThingTouchesOtherBadThing(x, y);
5327
5328     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5329       MovDir[x][y] = right_dir;
5330     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5331       MovDir[x][y] = left_dir;
5332
5333     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5334       MovDelay[x][y] = 9;
5335     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5336       MovDelay[x][y] = 1;
5337   }
5338   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5339   {
5340     TestIfBadThingTouchesOtherBadThing(x, y);
5341
5342     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5343       MovDir[x][y] = left_dir;
5344     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5345       MovDir[x][y] = right_dir;
5346
5347     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5348       MovDelay[x][y] = 9;
5349     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5350       MovDelay[x][y] = 1;
5351   }
5352   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5353   {
5354     TestIfBadThingTouchesOtherBadThing(x, y);
5355
5356     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5357       MovDir[x][y] = left_dir;
5358     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5359       MovDir[x][y] = right_dir;
5360
5361     if (MovDir[x][y] != old_move_dir)
5362       MovDelay[x][y] = 9;
5363   }
5364   else if (element == EL_YAMYAM)
5365   {
5366     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5367     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5368
5369     if (can_turn_left && can_turn_right)
5370       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5371     else if (can_turn_left)
5372       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5373     else if (can_turn_right)
5374       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5375     else
5376       MovDir[x][y] = back_dir;
5377
5378     MovDelay[x][y] = 16 + 16 * RND(3);
5379   }
5380   else if (element == EL_DARK_YAMYAM)
5381   {
5382     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5383                                                          left_x, left_y);
5384     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5385                                                          right_x, right_y);
5386
5387     if (can_turn_left && can_turn_right)
5388       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5389     else if (can_turn_left)
5390       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5391     else if (can_turn_right)
5392       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5393     else
5394       MovDir[x][y] = back_dir;
5395
5396     MovDelay[x][y] = 16 + 16 * RND(3);
5397   }
5398   else if (element == EL_PACMAN)
5399   {
5400     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5401     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5402
5403     if (can_turn_left && can_turn_right)
5404       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5405     else if (can_turn_left)
5406       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5407     else if (can_turn_right)
5408       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5409     else
5410       MovDir[x][y] = back_dir;
5411
5412     MovDelay[x][y] = 6 + RND(40);
5413   }
5414   else if (element == EL_PIG)
5415   {
5416     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5417     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5418     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5419     boolean should_turn_left, should_turn_right, should_move_on;
5420     int rnd_value = 24;
5421     int rnd = RND(rnd_value);
5422
5423     should_turn_left = (can_turn_left &&
5424                         (!can_move_on ||
5425                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5426                                                    y + back_dy + left_dy)));
5427     should_turn_right = (can_turn_right &&
5428                          (!can_move_on ||
5429                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5430                                                     y + back_dy + right_dy)));
5431     should_move_on = (can_move_on &&
5432                       (!can_turn_left ||
5433                        !can_turn_right ||
5434                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5435                                                  y + move_dy + left_dy) ||
5436                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5437                                                  y + move_dy + right_dy)));
5438
5439     if (should_turn_left || should_turn_right || should_move_on)
5440     {
5441       if (should_turn_left && should_turn_right && should_move_on)
5442         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5443                         rnd < 2 * rnd_value / 3 ? right_dir :
5444                         old_move_dir);
5445       else if (should_turn_left && should_turn_right)
5446         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5447       else if (should_turn_left && should_move_on)
5448         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5449       else if (should_turn_right && should_move_on)
5450         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5451       else if (should_turn_left)
5452         MovDir[x][y] = left_dir;
5453       else if (should_turn_right)
5454         MovDir[x][y] = right_dir;
5455       else if (should_move_on)
5456         MovDir[x][y] = old_move_dir;
5457     }
5458     else if (can_move_on && rnd > rnd_value / 8)
5459       MovDir[x][y] = old_move_dir;
5460     else if (can_turn_left && can_turn_right)
5461       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5462     else if (can_turn_left && rnd > rnd_value / 8)
5463       MovDir[x][y] = left_dir;
5464     else if (can_turn_right && rnd > rnd_value/8)
5465       MovDir[x][y] = right_dir;
5466     else
5467       MovDir[x][y] = back_dir;
5468
5469     xx = x + move_xy[MovDir[x][y]].dx;
5470     yy = y + move_xy[MovDir[x][y]].dy;
5471
5472     if (!IN_LEV_FIELD(xx, yy) ||
5473         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5474       MovDir[x][y] = old_move_dir;
5475
5476     MovDelay[x][y] = 0;
5477   }
5478   else if (element == EL_DRAGON)
5479   {
5480     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5481     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5482     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5483     int rnd_value = 24;
5484     int rnd = RND(rnd_value);
5485
5486     if (can_move_on && rnd > rnd_value / 8)
5487       MovDir[x][y] = old_move_dir;
5488     else if (can_turn_left && can_turn_right)
5489       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5490     else if (can_turn_left && rnd > rnd_value / 8)
5491       MovDir[x][y] = left_dir;
5492     else if (can_turn_right && rnd > rnd_value / 8)
5493       MovDir[x][y] = right_dir;
5494     else
5495       MovDir[x][y] = back_dir;
5496
5497     xx = x + move_xy[MovDir[x][y]].dx;
5498     yy = y + move_xy[MovDir[x][y]].dy;
5499
5500     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5501       MovDir[x][y] = old_move_dir;
5502
5503     MovDelay[x][y] = 0;
5504   }
5505   else if (element == EL_MOLE)
5506   {
5507     boolean can_move_on =
5508       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5509                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5510                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5511     if (!can_move_on)
5512     {
5513       boolean can_turn_left =
5514         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5515                               IS_AMOEBOID(Feld[left_x][left_y])));
5516
5517       boolean can_turn_right =
5518         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5519                               IS_AMOEBOID(Feld[right_x][right_y])));
5520
5521       if (can_turn_left && can_turn_right)
5522         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5523       else if (can_turn_left)
5524         MovDir[x][y] = left_dir;
5525       else
5526         MovDir[x][y] = right_dir;
5527     }
5528
5529     if (MovDir[x][y] != old_move_dir)
5530       MovDelay[x][y] = 9;
5531   }
5532   else if (element == EL_BALLOON)
5533   {
5534     MovDir[x][y] = game.wind_direction;
5535     MovDelay[x][y] = 0;
5536   }
5537   else if (element == EL_SPRING)
5538   {
5539 #if USE_NEW_SPRING_BUMPER
5540     if (MovDir[x][y] & MV_HORIZONTAL)
5541     {
5542       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5543           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5544       {
5545         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5546         ResetGfxAnimation(move_x, move_y);
5547         DrawLevelField(move_x, move_y);
5548
5549         MovDir[x][y] = back_dir;
5550       }
5551       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5552                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5553         MovDir[x][y] = MV_NONE;
5554     }
5555 #else
5556     if (MovDir[x][y] & MV_HORIZONTAL &&
5557         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5558          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5559       MovDir[x][y] = MV_NONE;
5560 #endif
5561
5562     MovDelay[x][y] = 0;
5563   }
5564   else if (element == EL_ROBOT ||
5565            element == EL_SATELLITE ||
5566            element == EL_PENGUIN ||
5567            element == EL_EMC_ANDROID)
5568   {
5569     int attr_x = -1, attr_y = -1;
5570
5571     if (AllPlayersGone)
5572     {
5573       attr_x = ExitX;
5574       attr_y = ExitY;
5575     }
5576     else
5577     {
5578       int i;
5579
5580       for (i = 0; i < MAX_PLAYERS; i++)
5581       {
5582         struct PlayerInfo *player = &stored_player[i];
5583         int jx = player->jx, jy = player->jy;
5584
5585         if (!player->active)
5586           continue;
5587
5588         if (attr_x == -1 ||
5589             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5590         {
5591           attr_x = jx;
5592           attr_y = jy;
5593         }
5594       }
5595     }
5596
5597     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5598         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5599          game.engine_version < VERSION_IDENT(3,1,0,0)))
5600     {
5601       attr_x = ZX;
5602       attr_y = ZY;
5603     }
5604
5605     if (element == EL_PENGUIN)
5606     {
5607       int i;
5608       static int xy[4][2] =
5609       {
5610         { 0, -1 },
5611         { -1, 0 },
5612         { +1, 0 },
5613         { 0, +1 }
5614       };
5615
5616       for (i = 0; i < NUM_DIRECTIONS; i++)
5617       {
5618         int ex = x + xy[i][0];
5619         int ey = y + xy[i][1];
5620
5621         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5622                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5623                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5624                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5625         {
5626           attr_x = ex;
5627           attr_y = ey;
5628           break;
5629         }
5630       }
5631     }
5632
5633     MovDir[x][y] = MV_NONE;
5634     if (attr_x < x)
5635       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5636     else if (attr_x > x)
5637       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5638     if (attr_y < y)
5639       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5640     else if (attr_y > y)
5641       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5642
5643     if (element == EL_ROBOT)
5644     {
5645       int newx, newy;
5646
5647       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5648         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5649       Moving2Blocked(x, y, &newx, &newy);
5650
5651       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5652         MovDelay[x][y] = 8 + 8 * !RND(3);
5653       else
5654         MovDelay[x][y] = 16;
5655     }
5656     else if (element == EL_PENGUIN)
5657     {
5658       int newx, newy;
5659
5660       MovDelay[x][y] = 1;
5661
5662       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5663       {
5664         boolean first_horiz = RND(2);
5665         int new_move_dir = MovDir[x][y];
5666
5667         MovDir[x][y] =
5668           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5669         Moving2Blocked(x, y, &newx, &newy);
5670
5671         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5672           return;
5673
5674         MovDir[x][y] =
5675           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5676         Moving2Blocked(x, y, &newx, &newy);
5677
5678         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5679           return;
5680
5681         MovDir[x][y] = old_move_dir;
5682         return;
5683       }
5684     }
5685     else if (element == EL_SATELLITE)
5686     {
5687       int newx, newy;
5688
5689       MovDelay[x][y] = 1;
5690
5691       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5692       {
5693         boolean first_horiz = RND(2);
5694         int new_move_dir = MovDir[x][y];
5695
5696         MovDir[x][y] =
5697           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5698         Moving2Blocked(x, y, &newx, &newy);
5699
5700         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5701           return;
5702
5703         MovDir[x][y] =
5704           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5705         Moving2Blocked(x, y, &newx, &newy);
5706
5707         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5708           return;
5709
5710         MovDir[x][y] = old_move_dir;
5711         return;
5712       }
5713     }
5714     else if (element == EL_EMC_ANDROID)
5715     {
5716       static int check_pos[16] =
5717       {
5718         -1,             /*  0 => (invalid)          */
5719         7,              /*  1 => MV_LEFT            */
5720         3,              /*  2 => MV_RIGHT           */
5721         -1,             /*  3 => (invalid)          */
5722         1,              /*  4 =>            MV_UP   */
5723         0,              /*  5 => MV_LEFT  | MV_UP   */
5724         2,              /*  6 => MV_RIGHT | MV_UP   */
5725         -1,             /*  7 => (invalid)          */
5726         5,              /*  8 =>            MV_DOWN */
5727         6,              /*  9 => MV_LEFT  | MV_DOWN */
5728         4,              /* 10 => MV_RIGHT | MV_DOWN */
5729         -1,             /* 11 => (invalid)          */
5730         -1,             /* 12 => (invalid)          */
5731         -1,             /* 13 => (invalid)          */
5732         -1,             /* 14 => (invalid)          */
5733         -1,             /* 15 => (invalid)          */
5734       };
5735       static struct
5736       {
5737         int dx, dy;
5738         int dir;
5739       } check_xy[8] =
5740       {
5741         { -1, -1,       MV_LEFT  | MV_UP   },
5742         {  0, -1,                  MV_UP   },
5743         { +1, -1,       MV_RIGHT | MV_UP   },
5744         { +1,  0,       MV_RIGHT           },
5745         { +1, +1,       MV_RIGHT | MV_DOWN },
5746         {  0, +1,                  MV_DOWN },
5747         { -1, +1,       MV_LEFT  | MV_DOWN },
5748         { -1,  0,       MV_LEFT            },
5749       };
5750       int start_pos, check_order;
5751       boolean can_clone = FALSE;
5752       int i;
5753
5754       /* check if there is any free field around current position */
5755       for (i = 0; i < 8; i++)
5756       {
5757         int newx = x + check_xy[i].dx;
5758         int newy = y + check_xy[i].dy;
5759
5760         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5761         {
5762           can_clone = TRUE;
5763
5764           break;
5765         }
5766       }
5767
5768       if (can_clone)            /* randomly find an element to clone */
5769       {
5770         can_clone = FALSE;
5771
5772         start_pos = check_pos[RND(8)];
5773         check_order = (RND(2) ? -1 : +1);
5774
5775         for (i = 0; i < 8; i++)
5776         {
5777           int pos_raw = start_pos + i * check_order;
5778           int pos = (pos_raw + 8) % 8;
5779           int newx = x + check_xy[pos].dx;
5780           int newy = y + check_xy[pos].dy;
5781
5782           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5783           {
5784             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5785             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5786
5787             Store[x][y] = Feld[newx][newy];
5788
5789             can_clone = TRUE;
5790
5791             break;
5792           }
5793         }
5794       }
5795
5796       if (can_clone)            /* randomly find a direction to move */
5797       {
5798         can_clone = FALSE;
5799
5800         start_pos = check_pos[RND(8)];
5801         check_order = (RND(2) ? -1 : +1);
5802
5803         for (i = 0; i < 8; i++)
5804         {
5805           int pos_raw = start_pos + i * check_order;
5806           int pos = (pos_raw + 8) % 8;
5807           int newx = x + check_xy[pos].dx;
5808           int newy = y + check_xy[pos].dy;
5809           int new_move_dir = check_xy[pos].dir;
5810
5811           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5812           {
5813             MovDir[x][y] = new_move_dir;
5814             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5815
5816             can_clone = TRUE;
5817
5818             break;
5819           }
5820         }
5821       }
5822
5823       if (can_clone)            /* cloning and moving successful */
5824         return;
5825
5826       /* cannot clone -- try to move towards player */
5827
5828       start_pos = check_pos[MovDir[x][y] & 0x0f];
5829       check_order = (RND(2) ? -1 : +1);
5830
5831       for (i = 0; i < 3; i++)
5832       {
5833         /* first check start_pos, then previous/next or (next/previous) pos */
5834         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5835         int pos = (pos_raw + 8) % 8;
5836         int newx = x + check_xy[pos].dx;
5837         int newy = y + check_xy[pos].dy;
5838         int new_move_dir = check_xy[pos].dir;
5839
5840         if (IS_PLAYER(newx, newy))
5841           break;
5842
5843         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5844         {
5845           MovDir[x][y] = new_move_dir;
5846           MovDelay[x][y] = level.android_move_time * 8 + 1;
5847
5848           break;
5849         }
5850       }
5851     }
5852   }
5853   else if (move_pattern == MV_TURNING_LEFT ||
5854            move_pattern == MV_TURNING_RIGHT ||
5855            move_pattern == MV_TURNING_LEFT_RIGHT ||
5856            move_pattern == MV_TURNING_RIGHT_LEFT ||
5857            move_pattern == MV_TURNING_RANDOM ||
5858            move_pattern == MV_ALL_DIRECTIONS)
5859   {
5860     boolean can_turn_left =
5861       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5862     boolean can_turn_right =
5863       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5864
5865     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5866       return;
5867
5868     if (move_pattern == MV_TURNING_LEFT)
5869       MovDir[x][y] = left_dir;
5870     else if (move_pattern == MV_TURNING_RIGHT)
5871       MovDir[x][y] = right_dir;
5872     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5873       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5874     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5875       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5876     else if (move_pattern == MV_TURNING_RANDOM)
5877       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5878                       can_turn_right && !can_turn_left ? right_dir :
5879                       RND(2) ? left_dir : right_dir);
5880     else if (can_turn_left && can_turn_right)
5881       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5882     else if (can_turn_left)
5883       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5884     else if (can_turn_right)
5885       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5886     else
5887       MovDir[x][y] = back_dir;
5888
5889     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5890   }
5891   else if (move_pattern == MV_HORIZONTAL ||
5892            move_pattern == MV_VERTICAL)
5893   {
5894     if (move_pattern & old_move_dir)
5895       MovDir[x][y] = back_dir;
5896     else if (move_pattern == MV_HORIZONTAL)
5897       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5898     else if (move_pattern == MV_VERTICAL)
5899       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5900
5901     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5902   }
5903   else if (move_pattern & MV_ANY_DIRECTION)
5904   {
5905     MovDir[x][y] = move_pattern;
5906     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5907   }
5908   else if (move_pattern & MV_WIND_DIRECTION)
5909   {
5910     MovDir[x][y] = game.wind_direction;
5911     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5912   }
5913   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5914   {
5915     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5916       MovDir[x][y] = left_dir;
5917     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5918       MovDir[x][y] = right_dir;
5919
5920     if (MovDir[x][y] != old_move_dir)
5921       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5922   }
5923   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5924   {
5925     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5926       MovDir[x][y] = right_dir;
5927     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5928       MovDir[x][y] = left_dir;
5929
5930     if (MovDir[x][y] != old_move_dir)
5931       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5932   }
5933   else if (move_pattern == MV_TOWARDS_PLAYER ||
5934            move_pattern == MV_AWAY_FROM_PLAYER)
5935   {
5936     int attr_x = -1, attr_y = -1;
5937     int newx, newy;
5938     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5939
5940     if (AllPlayersGone)
5941     {
5942       attr_x = ExitX;
5943       attr_y = ExitY;
5944     }
5945     else
5946     {
5947       int i;
5948
5949       for (i = 0; i < MAX_PLAYERS; i++)
5950       {
5951         struct PlayerInfo *player = &stored_player[i];
5952         int jx = player->jx, jy = player->jy;
5953
5954         if (!player->active)
5955           continue;
5956
5957         if (attr_x == -1 ||
5958             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5959         {
5960           attr_x = jx;
5961           attr_y = jy;
5962         }
5963       }
5964     }
5965
5966     MovDir[x][y] = MV_NONE;
5967     if (attr_x < x)
5968       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5969     else if (attr_x > x)
5970       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5971     if (attr_y < y)
5972       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5973     else if (attr_y > y)
5974       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5975
5976     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5977
5978     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5979     {
5980       boolean first_horiz = RND(2);
5981       int new_move_dir = MovDir[x][y];
5982
5983       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5984       {
5985         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5986         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5987
5988         return;
5989       }
5990
5991       MovDir[x][y] =
5992         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5993       Moving2Blocked(x, y, &newx, &newy);
5994
5995       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5996         return;
5997
5998       MovDir[x][y] =
5999         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6000       Moving2Blocked(x, y, &newx, &newy);
6001
6002       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6003         return;
6004
6005       MovDir[x][y] = old_move_dir;
6006     }
6007   }
6008   else if (move_pattern == MV_WHEN_PUSHED ||
6009            move_pattern == MV_WHEN_DROPPED)
6010   {
6011     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6012       MovDir[x][y] = MV_NONE;
6013
6014     MovDelay[x][y] = 0;
6015   }
6016   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6017   {
6018     static int test_xy[7][2] =
6019     {
6020       { 0, -1 },
6021       { -1, 0 },
6022       { +1, 0 },
6023       { 0, +1 },
6024       { 0, -1 },
6025       { -1, 0 },
6026       { +1, 0 },
6027     };
6028     static int test_dir[7] =
6029     {
6030       MV_UP,
6031       MV_LEFT,
6032       MV_RIGHT,
6033       MV_DOWN,
6034       MV_UP,
6035       MV_LEFT,
6036       MV_RIGHT,
6037     };
6038     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6039     int move_preference = -1000000;     /* start with very low preference */
6040     int new_move_dir = MV_NONE;
6041     int start_test = RND(4);
6042     int i;
6043
6044     for (i = 0; i < NUM_DIRECTIONS; i++)
6045     {
6046       int move_dir = test_dir[start_test + i];
6047       int move_dir_preference;
6048
6049       xx = x + test_xy[start_test + i][0];
6050       yy = y + test_xy[start_test + i][1];
6051
6052       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6053           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6054       {
6055         new_move_dir = move_dir;
6056
6057         break;
6058       }
6059
6060       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6061         continue;
6062
6063       move_dir_preference = -1 * RunnerVisit[xx][yy];
6064       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6065         move_dir_preference = PlayerVisit[xx][yy];
6066
6067       if (move_dir_preference > move_preference)
6068       {
6069         /* prefer field that has not been visited for the longest time */
6070         move_preference = move_dir_preference;
6071         new_move_dir = move_dir;
6072       }
6073       else if (move_dir_preference == move_preference &&
6074                move_dir == old_move_dir)
6075       {
6076         /* prefer last direction when all directions are preferred equally */
6077         move_preference = move_dir_preference;
6078         new_move_dir = move_dir;
6079       }
6080     }
6081
6082     MovDir[x][y] = new_move_dir;
6083     if (old_move_dir != new_move_dir)
6084       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6085   }
6086 }
6087
6088 static void TurnRound(int x, int y)
6089 {
6090   int direction = MovDir[x][y];
6091
6092   TurnRoundExt(x, y);
6093
6094   GfxDir[x][y] = MovDir[x][y];
6095
6096   if (direction != MovDir[x][y])
6097     GfxFrame[x][y] = 0;
6098
6099   if (MovDelay[x][y])
6100     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6101
6102   ResetGfxFrame(x, y, FALSE);
6103 }
6104
6105 static boolean JustBeingPushed(int x, int y)
6106 {
6107   int i;
6108
6109   for (i = 0; i < MAX_PLAYERS; i++)
6110   {
6111     struct PlayerInfo *player = &stored_player[i];
6112
6113     if (player->active && player->is_pushing && player->MovPos)
6114     {
6115       int next_jx = player->jx + (player->jx - player->last_jx);
6116       int next_jy = player->jy + (player->jy - player->last_jy);
6117
6118       if (x == next_jx && y == next_jy)
6119         return TRUE;
6120     }
6121   }
6122
6123   return FALSE;
6124 }
6125
6126 void StartMoving(int x, int y)
6127 {
6128   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6129   int element = Feld[x][y];
6130
6131   if (Stop[x][y])
6132     return;
6133
6134   if (MovDelay[x][y] == 0)
6135     GfxAction[x][y] = ACTION_DEFAULT;
6136
6137   if (CAN_FALL(element) && y < lev_fieldy - 1)
6138   {
6139     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6140         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6141       if (JustBeingPushed(x, y))
6142         return;
6143
6144     if (element == EL_QUICKSAND_FULL)
6145     {
6146       if (IS_FREE(x, y + 1))
6147       {
6148         InitMovingField(x, y, MV_DOWN);
6149         started_moving = TRUE;
6150
6151         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6152 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6153         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6154           Store[x][y] = EL_ROCK;
6155 #else
6156         Store[x][y] = EL_ROCK;
6157 #endif
6158
6159         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6160       }
6161       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6162       {
6163         if (!MovDelay[x][y])
6164           MovDelay[x][y] = TILEY + 1;
6165
6166         if (MovDelay[x][y])
6167         {
6168           MovDelay[x][y]--;
6169           if (MovDelay[x][y])
6170             return;
6171         }
6172
6173         Feld[x][y] = EL_QUICKSAND_EMPTY;
6174         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6175         Store[x][y + 1] = Store[x][y];
6176         Store[x][y] = 0;
6177
6178         PlayLevelSoundAction(x, y, ACTION_FILLING);
6179       }
6180     }
6181     else if (element == EL_QUICKSAND_FAST_FULL)
6182     {
6183       if (IS_FREE(x, y + 1))
6184       {
6185         InitMovingField(x, y, MV_DOWN);
6186         started_moving = TRUE;
6187
6188         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
6189 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6190         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6191           Store[x][y] = EL_ROCK;
6192 #else
6193         Store[x][y] = EL_ROCK;
6194 #endif
6195
6196         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6197       }
6198       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6199       {
6200         if (!MovDelay[x][y])
6201           MovDelay[x][y] = TILEY + 1;
6202
6203         if (MovDelay[x][y])
6204         {
6205           MovDelay[x][y]--;
6206           if (MovDelay[x][y])
6207             return;
6208         }
6209
6210         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
6211         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
6212         Store[x][y + 1] = Store[x][y];
6213         Store[x][y] = 0;
6214
6215         PlayLevelSoundAction(x, y, ACTION_FILLING);
6216       }
6217     }
6218     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6219              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6220     {
6221       InitMovingField(x, y, MV_DOWN);
6222       started_moving = TRUE;
6223
6224       Feld[x][y] = EL_QUICKSAND_FILLING;
6225       Store[x][y] = element;
6226
6227       PlayLevelSoundAction(x, y, ACTION_FILLING);
6228     }
6229     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6230              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6231     {
6232       InitMovingField(x, y, MV_DOWN);
6233       started_moving = TRUE;
6234
6235       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6236       Store[x][y] = element;
6237
6238       PlayLevelSoundAction(x, y, ACTION_FILLING);
6239     }
6240     else if (element == EL_MAGIC_WALL_FULL)
6241     {
6242       if (IS_FREE(x, y + 1))
6243       {
6244         InitMovingField(x, y, MV_DOWN);
6245         started_moving = TRUE;
6246
6247         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6248         Store[x][y] = EL_CHANGED(Store[x][y]);
6249       }
6250       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6251       {
6252         if (!MovDelay[x][y])
6253           MovDelay[x][y] = TILEY/4 + 1;
6254
6255         if (MovDelay[x][y])
6256         {
6257           MovDelay[x][y]--;
6258           if (MovDelay[x][y])
6259             return;
6260         }
6261
6262         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6263         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6264         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6265         Store[x][y] = 0;
6266       }
6267     }
6268     else if (element == EL_BD_MAGIC_WALL_FULL)
6269     {
6270       if (IS_FREE(x, y + 1))
6271       {
6272         InitMovingField(x, y, MV_DOWN);
6273         started_moving = TRUE;
6274
6275         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6276         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6277       }
6278       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6279       {
6280         if (!MovDelay[x][y])
6281           MovDelay[x][y] = TILEY/4 + 1;
6282
6283         if (MovDelay[x][y])
6284         {
6285           MovDelay[x][y]--;
6286           if (MovDelay[x][y])
6287             return;
6288         }
6289
6290         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6291         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6292         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6293         Store[x][y] = 0;
6294       }
6295     }
6296     else if (element == EL_DC_MAGIC_WALL_FULL)
6297     {
6298       if (IS_FREE(x, y + 1))
6299       {
6300         InitMovingField(x, y, MV_DOWN);
6301         started_moving = TRUE;
6302
6303         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6304         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6305       }
6306       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6307       {
6308         if (!MovDelay[x][y])
6309           MovDelay[x][y] = TILEY/4 + 1;
6310
6311         if (MovDelay[x][y])
6312         {
6313           MovDelay[x][y]--;
6314           if (MovDelay[x][y])
6315             return;
6316         }
6317
6318         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6319         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6320         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6321         Store[x][y] = 0;
6322       }
6323     }
6324     else if ((CAN_PASS_MAGIC_WALL(element) &&
6325               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6326                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6327              (CAN_PASS_DC_MAGIC_WALL(element) &&
6328               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6329
6330     {
6331       InitMovingField(x, y, MV_DOWN);
6332       started_moving = TRUE;
6333
6334       Feld[x][y] =
6335         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6336          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6337          EL_DC_MAGIC_WALL_FILLING);
6338       Store[x][y] = element;
6339     }
6340     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6341     {
6342       SplashAcid(x, y + 1);
6343
6344       InitMovingField(x, y, MV_DOWN);
6345       started_moving = TRUE;
6346
6347       Store[x][y] = EL_ACID;
6348     }
6349     else if (
6350 #if USE_FIX_IMPACT_COLLISION
6351              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6352               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6353 #else
6354              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6355               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6356 #endif
6357              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6358               CAN_FALL(element) && WasJustFalling[x][y] &&
6359               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6360
6361              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6362               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6363               (Feld[x][y + 1] == EL_BLOCKED)))
6364     {
6365       /* this is needed for a special case not covered by calling "Impact()"
6366          from "ContinueMoving()": if an element moves to a tile directly below
6367          another element which was just falling on that tile (which was empty
6368          in the previous frame), the falling element above would just stop
6369          instead of smashing the element below (in previous version, the above
6370          element was just checked for "moving" instead of "falling", resulting
6371          in incorrect smashes caused by horizontal movement of the above
6372          element; also, the case of the player being the element to smash was
6373          simply not covered here... :-/ ) */
6374
6375       CheckCollision[x][y] = 0;
6376       CheckImpact[x][y] = 0;
6377
6378       Impact(x, y);
6379     }
6380     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6381     {
6382       if (MovDir[x][y] == MV_NONE)
6383       {
6384         InitMovingField(x, y, MV_DOWN);
6385         started_moving = TRUE;
6386       }
6387     }
6388     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6389     {
6390       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6391         MovDir[x][y] = MV_DOWN;
6392
6393       InitMovingField(x, y, MV_DOWN);
6394       started_moving = TRUE;
6395     }
6396     else if (element == EL_AMOEBA_DROP)
6397     {
6398       Feld[x][y] = EL_AMOEBA_GROWING;
6399       Store[x][y] = EL_AMOEBA_WET;
6400     }
6401     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6402               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6403              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6404              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6405     {
6406       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6407                                 (IS_FREE(x - 1, y + 1) ||
6408                                  Feld[x - 1][y + 1] == EL_ACID));
6409       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6410                                 (IS_FREE(x + 1, y + 1) ||
6411                                  Feld[x + 1][y + 1] == EL_ACID));
6412       boolean can_fall_any  = (can_fall_left || can_fall_right);
6413       boolean can_fall_both = (can_fall_left && can_fall_right);
6414       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6415
6416 #if USE_NEW_ALL_SLIPPERY
6417       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6418       {
6419         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6420           can_fall_right = FALSE;
6421         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6422           can_fall_left = FALSE;
6423         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6424           can_fall_right = FALSE;
6425         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6426           can_fall_left = FALSE;
6427
6428         can_fall_any  = (can_fall_left || can_fall_right);
6429         can_fall_both = FALSE;
6430       }
6431 #else
6432       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6433       {
6434         if (slippery_type == SLIPPERY_ONLY_LEFT)
6435           can_fall_right = FALSE;
6436         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6437           can_fall_left = FALSE;
6438         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6439           can_fall_right = FALSE;
6440         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6441           can_fall_left = FALSE;
6442
6443         can_fall_any  = (can_fall_left || can_fall_right);
6444         can_fall_both = (can_fall_left && can_fall_right);
6445       }
6446 #endif
6447
6448 #if USE_NEW_ALL_SLIPPERY
6449 #else
6450 #if USE_NEW_SP_SLIPPERY
6451       /* !!! better use the same properties as for custom elements here !!! */
6452       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6453                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6454       {
6455         can_fall_right = FALSE;         /* slip down on left side */
6456         can_fall_both = FALSE;
6457       }
6458 #endif
6459 #endif
6460
6461 #if USE_NEW_ALL_SLIPPERY
6462       if (can_fall_both)
6463       {
6464         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6465           can_fall_right = FALSE;       /* slip down on left side */
6466         else
6467           can_fall_left = !(can_fall_right = RND(2));
6468
6469         can_fall_both = FALSE;
6470       }
6471 #else
6472       if (can_fall_both)
6473       {
6474         if (game.emulation == EMU_BOULDERDASH ||
6475             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6476           can_fall_right = FALSE;       /* slip down on left side */
6477         else
6478           can_fall_left = !(can_fall_right = RND(2));
6479
6480         can_fall_both = FALSE;
6481       }
6482 #endif
6483
6484       if (can_fall_any)
6485       {
6486         /* if not determined otherwise, prefer left side for slipping down */
6487         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6488         started_moving = TRUE;
6489       }
6490     }
6491 #if 0
6492     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6493 #else
6494     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6495 #endif
6496     {
6497       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6498       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6499       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6500       int belt_dir = game.belt_dir[belt_nr];
6501
6502       if ((belt_dir == MV_LEFT  && left_is_free) ||
6503           (belt_dir == MV_RIGHT && right_is_free))
6504       {
6505         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6506
6507         InitMovingField(x, y, belt_dir);
6508         started_moving = TRUE;
6509
6510         Pushed[x][y] = TRUE;
6511         Pushed[nextx][y] = TRUE;
6512
6513         GfxAction[x][y] = ACTION_DEFAULT;
6514       }
6515       else
6516       {
6517         MovDir[x][y] = 0;       /* if element was moving, stop it */
6518       }
6519     }
6520   }
6521
6522   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6523 #if 0
6524   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6525 #else
6526   if (CAN_MOVE(element) && !started_moving)
6527 #endif
6528   {
6529     int move_pattern = element_info[element].move_pattern;
6530     int newx, newy;
6531
6532 #if 0
6533 #if DEBUG
6534     if (MovDir[x][y] == MV_NONE)
6535     {
6536       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6537              x, y, element, element_info[element].token_name);
6538       printf("StartMoving(): This should never happen!\n");
6539     }
6540 #endif
6541 #endif
6542
6543     Moving2Blocked(x, y, &newx, &newy);
6544
6545     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6546       return;
6547
6548     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6549         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6550     {
6551       WasJustMoving[x][y] = 0;
6552       CheckCollision[x][y] = 0;
6553
6554       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6555
6556       if (Feld[x][y] != element)        /* element has changed */
6557         return;
6558     }
6559
6560     if (!MovDelay[x][y])        /* start new movement phase */
6561     {
6562       /* all objects that can change their move direction after each step
6563          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6564
6565       if (element != EL_YAMYAM &&
6566           element != EL_DARK_YAMYAM &&
6567           element != EL_PACMAN &&
6568           !(move_pattern & MV_ANY_DIRECTION) &&
6569           move_pattern != MV_TURNING_LEFT &&
6570           move_pattern != MV_TURNING_RIGHT &&
6571           move_pattern != MV_TURNING_LEFT_RIGHT &&
6572           move_pattern != MV_TURNING_RIGHT_LEFT &&
6573           move_pattern != MV_TURNING_RANDOM)
6574       {
6575         TurnRound(x, y);
6576
6577         if (MovDelay[x][y] && (element == EL_BUG ||
6578                                element == EL_SPACESHIP ||
6579                                element == EL_SP_SNIKSNAK ||
6580                                element == EL_SP_ELECTRON ||
6581                                element == EL_MOLE))
6582           DrawLevelField(x, y);
6583       }
6584     }
6585
6586     if (MovDelay[x][y])         /* wait some time before next movement */
6587     {
6588       MovDelay[x][y]--;
6589
6590       if (element == EL_ROBOT ||
6591           element == EL_YAMYAM ||
6592           element == EL_DARK_YAMYAM)
6593       {
6594         DrawLevelElementAnimationIfNeeded(x, y, element);
6595         PlayLevelSoundAction(x, y, ACTION_WAITING);
6596       }
6597       else if (element == EL_SP_ELECTRON)
6598         DrawLevelElementAnimationIfNeeded(x, y, element);
6599       else if (element == EL_DRAGON)
6600       {
6601         int i;
6602         int dir = MovDir[x][y];
6603         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6604         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6605         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6606                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6607                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6608                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6609         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6610
6611         GfxAction[x][y] = ACTION_ATTACKING;
6612
6613         if (IS_PLAYER(x, y))
6614           DrawPlayerField(x, y);
6615         else
6616           DrawLevelField(x, y);
6617
6618         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6619
6620         for (i = 1; i <= 3; i++)
6621         {
6622           int xx = x + i * dx;
6623           int yy = y + i * dy;
6624           int sx = SCREENX(xx);
6625           int sy = SCREENY(yy);
6626           int flame_graphic = graphic + (i - 1);
6627
6628           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6629             break;
6630
6631           if (MovDelay[x][y])
6632           {
6633             int flamed = MovingOrBlocked2Element(xx, yy);
6634
6635             /* !!! */
6636 #if 0
6637             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6638               Bang(xx, yy);
6639             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6640               RemoveMovingField(xx, yy);
6641             else
6642               RemoveField(xx, yy);
6643 #else
6644             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6645               Bang(xx, yy);
6646             else
6647               RemoveMovingField(xx, yy);
6648 #endif
6649
6650             ChangeDelay[xx][yy] = 0;
6651
6652             Feld[xx][yy] = EL_FLAMES;
6653
6654             if (IN_SCR_FIELD(sx, sy))
6655             {
6656               DrawLevelFieldCrumbledSand(xx, yy);
6657               DrawGraphic(sx, sy, flame_graphic, frame);
6658             }
6659           }
6660           else
6661           {
6662             if (Feld[xx][yy] == EL_FLAMES)
6663               Feld[xx][yy] = EL_EMPTY;
6664             DrawLevelField(xx, yy);
6665           }
6666         }
6667       }
6668
6669       if (MovDelay[x][y])       /* element still has to wait some time */
6670       {
6671         PlayLevelSoundAction(x, y, ACTION_WAITING);
6672
6673         return;
6674       }
6675     }
6676
6677     /* now make next step */
6678
6679     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6680
6681     if (DONT_COLLIDE_WITH(element) &&
6682         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6683         !PLAYER_ENEMY_PROTECTED(newx, newy))
6684     {
6685       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6686
6687       return;
6688     }
6689
6690     else if (CAN_MOVE_INTO_ACID(element) &&
6691              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6692              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6693              (MovDir[x][y] == MV_DOWN ||
6694               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6695     {
6696       SplashAcid(newx, newy);
6697       Store[x][y] = EL_ACID;
6698     }
6699     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6700     {
6701       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6702           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6703           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6704           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6705       {
6706         RemoveField(x, y);
6707         DrawLevelField(x, y);
6708
6709         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6710         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6711           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6712
6713         local_player->friends_still_needed--;
6714         if (!local_player->friends_still_needed &&
6715             !local_player->GameOver && AllPlayersGone)
6716           PlayerWins(local_player);
6717
6718         return;
6719       }
6720       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6721       {
6722         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6723           DrawLevelField(newx, newy);
6724         else
6725           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6726       }
6727       else if (!IS_FREE(newx, newy))
6728       {
6729         GfxAction[x][y] = ACTION_WAITING;
6730
6731         if (IS_PLAYER(x, y))
6732           DrawPlayerField(x, y);
6733         else
6734           DrawLevelField(x, y);
6735
6736         return;
6737       }
6738     }
6739     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6740     {
6741       if (IS_FOOD_PIG(Feld[newx][newy]))
6742       {
6743         if (IS_MOVING(newx, newy))
6744           RemoveMovingField(newx, newy);
6745         else
6746         {
6747           Feld[newx][newy] = EL_EMPTY;
6748           DrawLevelField(newx, newy);
6749         }
6750
6751         PlayLevelSound(x, y, SND_PIG_DIGGING);
6752       }
6753       else if (!IS_FREE(newx, newy))
6754       {
6755         if (IS_PLAYER(x, y))
6756           DrawPlayerField(x, y);
6757         else
6758           DrawLevelField(x, y);
6759
6760         return;
6761       }
6762     }
6763     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6764     {
6765       if (Store[x][y] != EL_EMPTY)
6766       {
6767         boolean can_clone = FALSE;
6768         int xx, yy;
6769
6770         /* check if element to clone is still there */
6771         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6772         {
6773           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6774           {
6775             can_clone = TRUE;
6776
6777             break;
6778           }
6779         }
6780
6781         /* cannot clone or target field not free anymore -- do not clone */
6782         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6783           Store[x][y] = EL_EMPTY;
6784       }
6785
6786       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6787       {
6788         if (IS_MV_DIAGONAL(MovDir[x][y]))
6789         {
6790           int diagonal_move_dir = MovDir[x][y];
6791           int stored = Store[x][y];
6792           int change_delay = 8;
6793           int graphic;
6794
6795           /* android is moving diagonally */
6796
6797           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6798
6799           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6800           GfxElement[x][y] = EL_EMC_ANDROID;
6801           GfxAction[x][y] = ACTION_SHRINKING;
6802           GfxDir[x][y] = diagonal_move_dir;
6803           ChangeDelay[x][y] = change_delay;
6804
6805           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6806                                    GfxDir[x][y]);
6807
6808           DrawLevelGraphicAnimation(x, y, graphic);
6809           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6810
6811           if (Feld[newx][newy] == EL_ACID)
6812           {
6813             SplashAcid(newx, newy);
6814
6815             return;
6816           }
6817
6818           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6819
6820           Store[newx][newy] = EL_EMC_ANDROID;
6821           GfxElement[newx][newy] = EL_EMC_ANDROID;
6822           GfxAction[newx][newy] = ACTION_GROWING;
6823           GfxDir[newx][newy] = diagonal_move_dir;
6824           ChangeDelay[newx][newy] = change_delay;
6825
6826           graphic = el_act_dir2img(GfxElement[newx][newy],
6827                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6828
6829           DrawLevelGraphicAnimation(newx, newy, graphic);
6830           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6831
6832           return;
6833         }
6834         else
6835         {
6836           Feld[newx][newy] = EL_EMPTY;
6837           DrawLevelField(newx, newy);
6838
6839           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6840         }
6841       }
6842       else if (!IS_FREE(newx, newy))
6843       {
6844 #if 0
6845         if (IS_PLAYER(x, y))
6846           DrawPlayerField(x, y);
6847         else
6848           DrawLevelField(x, y);
6849 #endif
6850
6851         return;
6852       }
6853     }
6854     else if (IS_CUSTOM_ELEMENT(element) &&
6855              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6856     {
6857       int new_element = Feld[newx][newy];
6858
6859       if (!IS_FREE(newx, newy))
6860       {
6861         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6862                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6863                       ACTION_BREAKING);
6864
6865         /* no element can dig solid indestructible elements */
6866         if (IS_INDESTRUCTIBLE(new_element) &&
6867             !IS_DIGGABLE(new_element) &&
6868             !IS_COLLECTIBLE(new_element))
6869           return;
6870
6871         if (AmoebaNr[newx][newy] &&
6872             (new_element == EL_AMOEBA_FULL ||
6873              new_element == EL_BD_AMOEBA ||
6874              new_element == EL_AMOEBA_GROWING))
6875         {
6876           AmoebaCnt[AmoebaNr[newx][newy]]--;
6877           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6878         }
6879
6880         if (IS_MOVING(newx, newy))
6881           RemoveMovingField(newx, newy);
6882         else
6883         {
6884           RemoveField(newx, newy);
6885           DrawLevelField(newx, newy);
6886         }
6887
6888         /* if digged element was about to explode, prevent the explosion */
6889         ExplodeField[newx][newy] = EX_TYPE_NONE;
6890
6891         PlayLevelSoundAction(x, y, action);
6892       }
6893
6894       Store[newx][newy] = EL_EMPTY;
6895 #if 1
6896       /* this makes it possible to leave the removed element again */
6897       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6898         Store[newx][newy] = new_element;
6899 #else
6900       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6901       {
6902         int move_leave_element = element_info[element].move_leave_element;
6903
6904         /* this makes it possible to leave the removed element again */
6905         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6906                              new_element : move_leave_element);
6907       }
6908 #endif
6909
6910       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6911       {
6912         RunnerVisit[x][y] = FrameCounter;
6913         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6914       }
6915     }
6916     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6917     {
6918       if (!IS_FREE(newx, newy))
6919       {
6920         if (IS_PLAYER(x, y))
6921           DrawPlayerField(x, y);
6922         else
6923           DrawLevelField(x, y);
6924
6925         return;
6926       }
6927       else
6928       {
6929         boolean wanna_flame = !RND(10);
6930         int dx = newx - x, dy = newy - y;
6931         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6932         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6933         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6934                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6935         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6936                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6937
6938         if ((wanna_flame ||
6939              IS_CLASSIC_ENEMY(element1) ||
6940              IS_CLASSIC_ENEMY(element2)) &&
6941             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6942             element1 != EL_FLAMES && element2 != EL_FLAMES)
6943         {
6944           ResetGfxAnimation(x, y);
6945           GfxAction[x][y] = ACTION_ATTACKING;
6946
6947           if (IS_PLAYER(x, y))
6948             DrawPlayerField(x, y);
6949           else
6950             DrawLevelField(x, y);
6951
6952           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6953
6954           MovDelay[x][y] = 50;
6955
6956           /* !!! */
6957 #if 0
6958           RemoveField(newx, newy);
6959 #endif
6960           Feld[newx][newy] = EL_FLAMES;
6961           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6962           {
6963 #if 0
6964             RemoveField(newx1, newy1);
6965 #endif
6966             Feld[newx1][newy1] = EL_FLAMES;
6967           }
6968           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6969           {
6970 #if 0
6971             RemoveField(newx2, newy2);
6972 #endif
6973             Feld[newx2][newy2] = EL_FLAMES;
6974           }
6975
6976           return;
6977         }
6978       }
6979     }
6980     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6981              Feld[newx][newy] == EL_DIAMOND)
6982     {
6983       if (IS_MOVING(newx, newy))
6984         RemoveMovingField(newx, newy);
6985       else
6986       {
6987         Feld[newx][newy] = EL_EMPTY;
6988         DrawLevelField(newx, newy);
6989       }
6990
6991       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6992     }
6993     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6994              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6995     {
6996       if (AmoebaNr[newx][newy])
6997       {
6998         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6999         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7000             Feld[newx][newy] == EL_BD_AMOEBA)
7001           AmoebaCnt[AmoebaNr[newx][newy]]--;
7002       }
7003
7004 #if 0
7005       /* !!! test !!! */
7006       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7007       {
7008         RemoveMovingField(newx, newy);
7009       }
7010 #else
7011       if (IS_MOVING(newx, newy))
7012       {
7013         RemoveMovingField(newx, newy);
7014       }
7015 #endif
7016       else
7017       {
7018         Feld[newx][newy] = EL_EMPTY;
7019         DrawLevelField(newx, newy);
7020       }
7021
7022       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7023     }
7024     else if ((element == EL_PACMAN || element == EL_MOLE)
7025              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7026     {
7027       if (AmoebaNr[newx][newy])
7028       {
7029         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7030         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7031             Feld[newx][newy] == EL_BD_AMOEBA)
7032           AmoebaCnt[AmoebaNr[newx][newy]]--;
7033       }
7034
7035       if (element == EL_MOLE)
7036       {
7037         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7038         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7039
7040         ResetGfxAnimation(x, y);
7041         GfxAction[x][y] = ACTION_DIGGING;
7042         DrawLevelField(x, y);
7043
7044         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7045
7046         return;                         /* wait for shrinking amoeba */
7047       }
7048       else      /* element == EL_PACMAN */
7049       {
7050         Feld[newx][newy] = EL_EMPTY;
7051         DrawLevelField(newx, newy);
7052         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7053       }
7054     }
7055     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7056              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7057               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7058     {
7059       /* wait for shrinking amoeba to completely disappear */
7060       return;
7061     }
7062     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7063     {
7064       /* object was running against a wall */
7065
7066       TurnRound(x, y);
7067
7068 #if 0
7069       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7070       if (move_pattern & MV_ANY_DIRECTION &&
7071           move_pattern == MovDir[x][y])
7072       {
7073         int blocking_element =
7074           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7075
7076         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7077                                  MovDir[x][y]);
7078
7079         element = Feld[x][y];   /* element might have changed */
7080       }
7081 #endif
7082
7083       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7084         DrawLevelElementAnimation(x, y, element);
7085
7086       if (DONT_TOUCH(element))
7087         TestIfBadThingTouchesPlayer(x, y);
7088
7089       return;
7090     }
7091
7092     InitMovingField(x, y, MovDir[x][y]);
7093
7094     PlayLevelSoundAction(x, y, ACTION_MOVING);
7095   }
7096
7097   if (MovDir[x][y])
7098     ContinueMoving(x, y);
7099 }
7100
7101 void ContinueMoving(int x, int y)
7102 {
7103   int element = Feld[x][y];
7104   struct ElementInfo *ei = &element_info[element];
7105   int direction = MovDir[x][y];
7106   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7107   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7108   int newx = x + dx, newy = y + dy;
7109   int stored = Store[x][y];
7110   int stored_new = Store[newx][newy];
7111   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7112   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7113   boolean last_line = (newy == lev_fieldy - 1);
7114
7115   MovPos[x][y] += getElementMoveStepsize(x, y);
7116
7117   if (pushed_by_player) /* special case: moving object pushed by player */
7118     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7119
7120   if (ABS(MovPos[x][y]) < TILEX)
7121   {
7122 #if 0
7123     int ee = Feld[x][y];
7124     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7125     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7126
7127     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7128            x, y, ABS(MovPos[x][y]),
7129            ee, gg, ff,
7130            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7131 #endif
7132
7133     DrawLevelField(x, y);
7134
7135     return;     /* element is still moving */
7136   }
7137
7138   /* element reached destination field */
7139
7140   Feld[x][y] = EL_EMPTY;
7141   Feld[newx][newy] = element;
7142   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7143
7144   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7145   {
7146     element = Feld[newx][newy] = EL_ACID;
7147   }
7148   else if (element == EL_MOLE)
7149   {
7150     Feld[x][y] = EL_SAND;
7151
7152     DrawLevelFieldCrumbledSandNeighbours(x, y);
7153   }
7154   else if (element == EL_QUICKSAND_FILLING)
7155   {
7156     element = Feld[newx][newy] = get_next_element(element);
7157     Store[newx][newy] = Store[x][y];
7158   }
7159   else if (element == EL_QUICKSAND_EMPTYING)
7160   {
7161     Feld[x][y] = get_next_element(element);
7162     element = Feld[newx][newy] = Store[x][y];
7163   }
7164   else if (element == EL_QUICKSAND_FAST_FILLING)
7165   {
7166     element = Feld[newx][newy] = get_next_element(element);
7167     Store[newx][newy] = Store[x][y];
7168   }
7169   else if (element == EL_QUICKSAND_FAST_EMPTYING)
7170   {
7171     Feld[x][y] = get_next_element(element);
7172     element = Feld[newx][newy] = Store[x][y];
7173   }
7174   else if (element == EL_MAGIC_WALL_FILLING)
7175   {
7176     element = Feld[newx][newy] = get_next_element(element);
7177     if (!game.magic_wall_active)
7178       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7179     Store[newx][newy] = Store[x][y];
7180   }
7181   else if (element == EL_MAGIC_WALL_EMPTYING)
7182   {
7183     Feld[x][y] = get_next_element(element);
7184     if (!game.magic_wall_active)
7185       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7186     element = Feld[newx][newy] = Store[x][y];
7187
7188 #if USE_NEW_CUSTOM_VALUE
7189     InitField(newx, newy, FALSE);
7190 #endif
7191   }
7192   else if (element == EL_BD_MAGIC_WALL_FILLING)
7193   {
7194     element = Feld[newx][newy] = get_next_element(element);
7195     if (!game.magic_wall_active)
7196       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7197     Store[newx][newy] = Store[x][y];
7198   }
7199   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7200   {
7201     Feld[x][y] = get_next_element(element);
7202     if (!game.magic_wall_active)
7203       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7204     element = Feld[newx][newy] = Store[x][y];
7205
7206 #if USE_NEW_CUSTOM_VALUE
7207     InitField(newx, newy, FALSE);
7208 #endif
7209   }
7210   else if (element == EL_DC_MAGIC_WALL_FILLING)
7211   {
7212     element = Feld[newx][newy] = get_next_element(element);
7213     if (!game.magic_wall_active)
7214       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
7215     Store[newx][newy] = Store[x][y];
7216   }
7217   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
7218   {
7219     Feld[x][y] = get_next_element(element);
7220     if (!game.magic_wall_active)
7221       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
7222     element = Feld[newx][newy] = Store[x][y];
7223
7224 #if USE_NEW_CUSTOM_VALUE
7225     InitField(newx, newy, FALSE);
7226 #endif
7227   }
7228   else if (element == EL_AMOEBA_DROPPING)
7229   {
7230     Feld[x][y] = get_next_element(element);
7231     element = Feld[newx][newy] = Store[x][y];
7232   }
7233   else if (element == EL_SOKOBAN_OBJECT)
7234   {
7235     if (Back[x][y])
7236       Feld[x][y] = Back[x][y];
7237
7238     if (Back[newx][newy])
7239       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7240
7241     Back[x][y] = Back[newx][newy] = 0;
7242   }
7243
7244   Store[x][y] = EL_EMPTY;
7245   MovPos[x][y] = 0;
7246   MovDir[x][y] = 0;
7247   MovDelay[x][y] = 0;
7248
7249   MovDelay[newx][newy] = 0;
7250
7251   if (CAN_CHANGE_OR_HAS_ACTION(element))
7252   {
7253     /* copy element change control values to new field */
7254     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7255     ChangePage[newx][newy]  = ChangePage[x][y];
7256     ChangeCount[newx][newy] = ChangeCount[x][y];
7257     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7258   }
7259
7260 #if USE_NEW_CUSTOM_VALUE
7261     CustomValue[newx][newy] = CustomValue[x][y];
7262 #endif
7263
7264   ChangeDelay[x][y] = 0;
7265   ChangePage[x][y] = -1;
7266   ChangeCount[x][y] = 0;
7267   ChangeEvent[x][y] = -1;
7268
7269 #if USE_NEW_CUSTOM_VALUE
7270   CustomValue[x][y] = 0;
7271 #endif
7272
7273   /* copy animation control values to new field */
7274   GfxFrame[newx][newy]  = GfxFrame[x][y];
7275   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7276   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7277   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7278
7279   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7280
7281   /* some elements can leave other elements behind after moving */
7282 #if 1
7283   if (ei->move_leave_element != EL_EMPTY &&
7284       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7285       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7286 #else
7287   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7288       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7289       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7290 #endif
7291   {
7292     int move_leave_element = ei->move_leave_element;
7293
7294 #if 1
7295 #if 1
7296     /* this makes it possible to leave the removed element again */
7297     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7298       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7299 #else
7300     /* this makes it possible to leave the removed element again */
7301     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7302       move_leave_element = stored;
7303 #endif
7304 #else
7305     /* this makes it possible to leave the removed element again */
7306     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7307         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7308       move_leave_element = stored;
7309 #endif
7310
7311     Feld[x][y] = move_leave_element;
7312
7313     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7314       MovDir[x][y] = direction;
7315
7316     InitField(x, y, FALSE);
7317
7318     if (GFX_CRUMBLED(Feld[x][y]))
7319       DrawLevelFieldCrumbledSandNeighbours(x, y);
7320
7321     if (ELEM_IS_PLAYER(move_leave_element))
7322       RelocatePlayer(x, y, move_leave_element);
7323   }
7324
7325   /* do this after checking for left-behind element */
7326   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7327
7328   if (!CAN_MOVE(element) ||
7329       (CAN_FALL(element) && direction == MV_DOWN &&
7330        (element == EL_SPRING ||
7331         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7332         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7333     GfxDir[x][y] = MovDir[newx][newy] = 0;
7334
7335   DrawLevelField(x, y);
7336   DrawLevelField(newx, newy);
7337
7338   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7339
7340   /* prevent pushed element from moving on in pushed direction */
7341   if (pushed_by_player && CAN_MOVE(element) &&
7342       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7343       !(element_info[element].move_pattern & direction))
7344     TurnRound(newx, newy);
7345
7346   /* prevent elements on conveyor belt from moving on in last direction */
7347   if (pushed_by_conveyor && CAN_FALL(element) &&
7348       direction & MV_HORIZONTAL)
7349     MovDir[newx][newy] = 0;
7350
7351   if (!pushed_by_player)
7352   {
7353     int nextx = newx + dx, nexty = newy + dy;
7354     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7355
7356     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7357
7358     if (CAN_FALL(element) && direction == MV_DOWN)
7359       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7360
7361     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7362       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7363
7364 #if USE_FIX_IMPACT_COLLISION
7365     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7366       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7367 #endif
7368   }
7369
7370   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7371   {
7372     TestIfBadThingTouchesPlayer(newx, newy);
7373     TestIfBadThingTouchesFriend(newx, newy);
7374
7375     if (!IS_CUSTOM_ELEMENT(element))
7376       TestIfBadThingTouchesOtherBadThing(newx, newy);
7377   }
7378   else if (element == EL_PENGUIN)
7379     TestIfFriendTouchesBadThing(newx, newy);
7380
7381   /* give the player one last chance (one more frame) to move away */
7382   if (CAN_FALL(element) && direction == MV_DOWN &&
7383       (last_line || (!IS_FREE(x, newy + 1) &&
7384                      (!IS_PLAYER(x, newy + 1) ||
7385                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7386     Impact(x, newy);
7387
7388   if (pushed_by_player && !game.use_change_when_pushing_bug)
7389   {
7390     int push_side = MV_DIR_OPPOSITE(direction);
7391     struct PlayerInfo *player = PLAYERINFO(x, y);
7392
7393     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7394                                player->index_bit, push_side);
7395     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7396                                         player->index_bit, push_side);
7397   }
7398
7399   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7400     MovDelay[newx][newy] = 1;
7401
7402   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7403
7404   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7405
7406 #if 0
7407   if (ChangePage[newx][newy] != -1)             /* delayed change */
7408   {
7409     int page = ChangePage[newx][newy];
7410     struct ElementChangeInfo *change = &ei->change_page[page];
7411
7412     ChangePage[newx][newy] = -1;
7413
7414     if (change->can_change)
7415     {
7416       if (ChangeElement(newx, newy, element, page))
7417       {
7418         if (change->post_change_function)
7419           change->post_change_function(newx, newy);
7420       }
7421     }
7422
7423     if (change->has_action)
7424       ExecuteCustomElementAction(newx, newy, element, page);
7425   }
7426 #endif
7427
7428   TestIfElementHitsCustomElement(newx, newy, direction);
7429   TestIfPlayerTouchesCustomElement(newx, newy);
7430   TestIfElementTouchesCustomElement(newx, newy);
7431
7432   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7433       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7434     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7435                              MV_DIR_OPPOSITE(direction));
7436 }
7437
7438 int AmoebeNachbarNr(int ax, int ay)
7439 {
7440   int i;
7441   int element = Feld[ax][ay];
7442   int group_nr = 0;
7443   static int xy[4][2] =
7444   {
7445     { 0, -1 },
7446     { -1, 0 },
7447     { +1, 0 },
7448     { 0, +1 }
7449   };
7450
7451   for (i = 0; i < NUM_DIRECTIONS; i++)
7452   {
7453     int x = ax + xy[i][0];
7454     int y = ay + xy[i][1];
7455
7456     if (!IN_LEV_FIELD(x, y))
7457       continue;
7458
7459     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7460       group_nr = AmoebaNr[x][y];
7461   }
7462
7463   return group_nr;
7464 }
7465
7466 void AmoebenVereinigen(int ax, int ay)
7467 {
7468   int i, x, y, xx, yy;
7469   int new_group_nr = AmoebaNr[ax][ay];
7470   static int xy[4][2] =
7471   {
7472     { 0, -1 },
7473     { -1, 0 },
7474     { +1, 0 },
7475     { 0, +1 }
7476   };
7477
7478   if (new_group_nr == 0)
7479     return;
7480
7481   for (i = 0; i < NUM_DIRECTIONS; i++)
7482   {
7483     x = ax + xy[i][0];
7484     y = ay + xy[i][1];
7485
7486     if (!IN_LEV_FIELD(x, y))
7487       continue;
7488
7489     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7490          Feld[x][y] == EL_BD_AMOEBA ||
7491          Feld[x][y] == EL_AMOEBA_DEAD) &&
7492         AmoebaNr[x][y] != new_group_nr)
7493     {
7494       int old_group_nr = AmoebaNr[x][y];
7495
7496       if (old_group_nr == 0)
7497         return;
7498
7499       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7500       AmoebaCnt[old_group_nr] = 0;
7501       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7502       AmoebaCnt2[old_group_nr] = 0;
7503
7504       SCAN_PLAYFIELD(xx, yy)
7505       {
7506         if (AmoebaNr[xx][yy] == old_group_nr)
7507           AmoebaNr[xx][yy] = new_group_nr;
7508       }
7509     }
7510   }
7511 }
7512
7513 void AmoebeUmwandeln(int ax, int ay)
7514 {
7515   int i, x, y;
7516
7517   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7518   {
7519     int group_nr = AmoebaNr[ax][ay];
7520
7521 #ifdef DEBUG
7522     if (group_nr == 0)
7523     {
7524       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7525       printf("AmoebeUmwandeln(): This should never happen!\n");
7526       return;
7527     }
7528 #endif
7529
7530     SCAN_PLAYFIELD(x, y)
7531     {
7532       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7533       {
7534         AmoebaNr[x][y] = 0;
7535         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7536       }
7537     }
7538
7539     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7540                             SND_AMOEBA_TURNING_TO_GEM :
7541                             SND_AMOEBA_TURNING_TO_ROCK));
7542     Bang(ax, ay);
7543   }
7544   else
7545   {
7546     static int xy[4][2] =
7547     {
7548       { 0, -1 },
7549       { -1, 0 },
7550       { +1, 0 },
7551       { 0, +1 }
7552     };
7553
7554     for (i = 0; i < NUM_DIRECTIONS; i++)
7555     {
7556       x = ax + xy[i][0];
7557       y = ay + xy[i][1];
7558
7559       if (!IN_LEV_FIELD(x, y))
7560         continue;
7561
7562       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7563       {
7564         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7565                               SND_AMOEBA_TURNING_TO_GEM :
7566                               SND_AMOEBA_TURNING_TO_ROCK));
7567         Bang(x, y);
7568       }
7569     }
7570   }
7571 }
7572
7573 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7574 {
7575   int x, y;
7576   int group_nr = AmoebaNr[ax][ay];
7577   boolean done = FALSE;
7578
7579 #ifdef DEBUG
7580   if (group_nr == 0)
7581   {
7582     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7583     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7584     return;
7585   }
7586 #endif
7587
7588   SCAN_PLAYFIELD(x, y)
7589   {
7590     if (AmoebaNr[x][y] == group_nr &&
7591         (Feld[x][y] == EL_AMOEBA_DEAD ||
7592          Feld[x][y] == EL_BD_AMOEBA ||
7593          Feld[x][y] == EL_AMOEBA_GROWING))
7594     {
7595       AmoebaNr[x][y] = 0;
7596       Feld[x][y] = new_element;
7597       InitField(x, y, FALSE);
7598       DrawLevelField(x, y);
7599       done = TRUE;
7600     }
7601   }
7602
7603   if (done)
7604     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7605                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7606                             SND_BD_AMOEBA_TURNING_TO_GEM));
7607 }
7608
7609 void AmoebeWaechst(int x, int y)
7610 {
7611   static unsigned long sound_delay = 0;
7612   static unsigned long sound_delay_value = 0;
7613
7614   if (!MovDelay[x][y])          /* start new growing cycle */
7615   {
7616     MovDelay[x][y] = 7;
7617
7618     if (DelayReached(&sound_delay, sound_delay_value))
7619     {
7620       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7621       sound_delay_value = 30;
7622     }
7623   }
7624
7625   if (MovDelay[x][y])           /* wait some time before growing bigger */
7626   {
7627     MovDelay[x][y]--;
7628     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7629     {
7630       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7631                                            6 - MovDelay[x][y]);
7632
7633       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7634     }
7635
7636     if (!MovDelay[x][y])
7637     {
7638       Feld[x][y] = Store[x][y];
7639       Store[x][y] = 0;
7640       DrawLevelField(x, y);
7641     }
7642   }
7643 }
7644
7645 void AmoebaDisappearing(int x, int y)
7646 {
7647   static unsigned long sound_delay = 0;
7648   static unsigned long sound_delay_value = 0;
7649
7650   if (!MovDelay[x][y])          /* start new shrinking cycle */
7651   {
7652     MovDelay[x][y] = 7;
7653
7654     if (DelayReached(&sound_delay, sound_delay_value))
7655       sound_delay_value = 30;
7656   }
7657
7658   if (MovDelay[x][y])           /* wait some time before shrinking */
7659   {
7660     MovDelay[x][y]--;
7661     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7662     {
7663       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7664                                            6 - MovDelay[x][y]);
7665
7666       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7667     }
7668
7669     if (!MovDelay[x][y])
7670     {
7671       Feld[x][y] = EL_EMPTY;
7672       DrawLevelField(x, y);
7673
7674       /* don't let mole enter this field in this cycle;
7675          (give priority to objects falling to this field from above) */
7676       Stop[x][y] = TRUE;
7677     }
7678   }
7679 }
7680
7681 void AmoebeAbleger(int ax, int ay)
7682 {
7683   int i;
7684   int element = Feld[ax][ay];
7685   int graphic = el2img(element);
7686   int newax = ax, neway = ay;
7687   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7688   static int xy[4][2] =
7689   {
7690     { 0, -1 },
7691     { -1, 0 },
7692     { +1, 0 },
7693     { 0, +1 }
7694   };
7695
7696   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7697   {
7698     Feld[ax][ay] = EL_AMOEBA_DEAD;
7699     DrawLevelField(ax, ay);
7700     return;
7701   }
7702
7703   if (IS_ANIMATED(graphic))
7704     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7705
7706   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7707     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7708
7709   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7710   {
7711     MovDelay[ax][ay]--;
7712     if (MovDelay[ax][ay])
7713       return;
7714   }
7715
7716   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7717   {
7718     int start = RND(4);
7719     int x = ax + xy[start][0];
7720     int y = ay + xy[start][1];
7721
7722     if (!IN_LEV_FIELD(x, y))
7723       return;
7724
7725     if (IS_FREE(x, y) ||
7726         CAN_GROW_INTO(Feld[x][y]) ||
7727         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7728         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7729     {
7730       newax = x;
7731       neway = y;
7732     }
7733
7734     if (newax == ax && neway == ay)
7735       return;
7736   }
7737   else                          /* normal or "filled" (BD style) amoeba */
7738   {
7739     int start = RND(4);
7740     boolean waiting_for_player = FALSE;
7741
7742     for (i = 0; i < NUM_DIRECTIONS; i++)
7743     {
7744       int j = (start + i) % 4;
7745       int x = ax + xy[j][0];
7746       int y = ay + xy[j][1];
7747
7748       if (!IN_LEV_FIELD(x, y))
7749         continue;
7750
7751       if (IS_FREE(x, y) ||
7752           CAN_GROW_INTO(Feld[x][y]) ||
7753           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7754           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7755       {
7756         newax = x;
7757         neway = y;
7758         break;
7759       }
7760       else if (IS_PLAYER(x, y))
7761         waiting_for_player = TRUE;
7762     }
7763
7764     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7765     {
7766       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7767       {
7768         Feld[ax][ay] = EL_AMOEBA_DEAD;
7769         DrawLevelField(ax, ay);
7770         AmoebaCnt[AmoebaNr[ax][ay]]--;
7771
7772         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7773         {
7774           if (element == EL_AMOEBA_FULL)
7775             AmoebeUmwandeln(ax, ay);
7776           else if (element == EL_BD_AMOEBA)
7777             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7778         }
7779       }
7780       return;
7781     }
7782     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7783     {
7784       /* amoeba gets larger by growing in some direction */
7785
7786       int new_group_nr = AmoebaNr[ax][ay];
7787
7788 #ifdef DEBUG
7789   if (new_group_nr == 0)
7790   {
7791     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7792     printf("AmoebeAbleger(): This should never happen!\n");
7793     return;
7794   }
7795 #endif
7796
7797       AmoebaNr[newax][neway] = new_group_nr;
7798       AmoebaCnt[new_group_nr]++;
7799       AmoebaCnt2[new_group_nr]++;
7800
7801       /* if amoeba touches other amoeba(s) after growing, unify them */
7802       AmoebenVereinigen(newax, neway);
7803
7804       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7805       {
7806         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7807         return;
7808       }
7809     }
7810   }
7811
7812   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7813       (neway == lev_fieldy - 1 && newax != ax))
7814   {
7815     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7816     Store[newax][neway] = element;
7817   }
7818   else if (neway == ay || element == EL_EMC_DRIPPER)
7819   {
7820     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7821
7822     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7823   }
7824   else
7825   {
7826     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7827     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7828     Store[ax][ay] = EL_AMOEBA_DROP;
7829     ContinueMoving(ax, ay);
7830     return;
7831   }
7832
7833   DrawLevelField(newax, neway);
7834 }
7835
7836 void Life(int ax, int ay)
7837 {
7838   int x1, y1, x2, y2;
7839   int life_time = 40;
7840   int element = Feld[ax][ay];
7841   int graphic = el2img(element);
7842   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7843                          level.biomaze);
7844   boolean changed = FALSE;
7845
7846   if (IS_ANIMATED(graphic))
7847     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7848
7849   if (Stop[ax][ay])
7850     return;
7851
7852   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7853     MovDelay[ax][ay] = life_time;
7854
7855   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7856   {
7857     MovDelay[ax][ay]--;
7858     if (MovDelay[ax][ay])
7859       return;
7860   }
7861
7862   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7863   {
7864     int xx = ax+x1, yy = ay+y1;
7865     int nachbarn = 0;
7866
7867     if (!IN_LEV_FIELD(xx, yy))
7868       continue;
7869
7870     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7871     {
7872       int x = xx+x2, y = yy+y2;
7873
7874       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7875         continue;
7876
7877       if (((Feld[x][y] == element ||
7878             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7879            !Stop[x][y]) ||
7880           (IS_FREE(x, y) && Stop[x][y]))
7881         nachbarn++;
7882     }
7883
7884     if (xx == ax && yy == ay)           /* field in the middle */
7885     {
7886       if (nachbarn < life_parameter[0] ||
7887           nachbarn > life_parameter[1])
7888       {
7889         Feld[xx][yy] = EL_EMPTY;
7890         if (!Stop[xx][yy])
7891           DrawLevelField(xx, yy);
7892         Stop[xx][yy] = TRUE;
7893         changed = TRUE;
7894       }
7895     }
7896     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7897     {                                   /* free border field */
7898       if (nachbarn >= life_parameter[2] &&
7899           nachbarn <= life_parameter[3])
7900       {
7901         Feld[xx][yy] = element;
7902         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7903         if (!Stop[xx][yy])
7904           DrawLevelField(xx, yy);
7905         Stop[xx][yy] = TRUE;
7906         changed = TRUE;
7907       }
7908     }
7909   }
7910
7911   if (changed)
7912     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7913                    SND_GAME_OF_LIFE_GROWING);
7914 }
7915
7916 static void InitRobotWheel(int x, int y)
7917 {
7918   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7919 }
7920
7921 static void RunRobotWheel(int x, int y)
7922 {
7923   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7924 }
7925
7926 static void StopRobotWheel(int x, int y)
7927 {
7928   if (ZX == x && ZY == y)
7929     ZX = ZY = -1;
7930 }
7931
7932 static void InitTimegateWheel(int x, int y)
7933 {
7934   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7935 }
7936
7937 static void RunTimegateWheel(int x, int y)
7938 {
7939   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7940 }
7941
7942 static void InitMagicBallDelay(int x, int y)
7943 {
7944 #if 1
7945   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7946 #else
7947   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7948 #endif
7949 }
7950
7951 static void ActivateMagicBall(int bx, int by)
7952 {
7953   int x, y;
7954
7955   if (level.ball_random)
7956   {
7957     int pos_border = RND(8);    /* select one of the eight border elements */
7958     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7959     int xx = pos_content % 3;
7960     int yy = pos_content / 3;
7961
7962     x = bx - 1 + xx;
7963     y = by - 1 + yy;
7964
7965     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7966       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7967   }
7968   else
7969   {
7970     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7971     {
7972       int xx = x - bx + 1;
7973       int yy = y - by + 1;
7974
7975       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7976         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7977     }
7978   }
7979
7980   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7981 }
7982
7983 void CheckExit(int x, int y)
7984 {
7985   if (local_player->gems_still_needed > 0 ||
7986       local_player->sokobanfields_still_needed > 0 ||
7987       local_player->lights_still_needed > 0)
7988   {
7989     int element = Feld[x][y];
7990     int graphic = el2img(element);
7991
7992     if (IS_ANIMATED(graphic))
7993       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7994
7995     return;
7996   }
7997
7998   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7999     return;
8000
8001   Feld[x][y] = EL_EXIT_OPENING;
8002
8003   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8004 }
8005
8006 void CheckExitEM(int x, int y)
8007 {
8008   if (local_player->gems_still_needed > 0 ||
8009       local_player->sokobanfields_still_needed > 0 ||
8010       local_player->lights_still_needed > 0)
8011   {
8012     int element = Feld[x][y];
8013     int graphic = el2img(element);
8014
8015     if (IS_ANIMATED(graphic))
8016       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8017
8018     return;
8019   }
8020
8021   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8022     return;
8023
8024   Feld[x][y] = EL_EM_EXIT_OPENING;
8025
8026   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8027 }
8028
8029 void CheckExitSteel(int x, int y)
8030 {
8031   if (local_player->gems_still_needed > 0 ||
8032       local_player->sokobanfields_still_needed > 0 ||
8033       local_player->lights_still_needed > 0)
8034   {
8035     int element = Feld[x][y];
8036     int graphic = el2img(element);
8037
8038     if (IS_ANIMATED(graphic))
8039       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8040
8041     return;
8042   }
8043
8044   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8045     return;
8046
8047   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8048
8049   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8050 }
8051
8052 void CheckExitSteelEM(int x, int y)
8053 {
8054   if (local_player->gems_still_needed > 0 ||
8055       local_player->sokobanfields_still_needed > 0 ||
8056       local_player->lights_still_needed > 0)
8057   {
8058     int element = Feld[x][y];
8059     int graphic = el2img(element);
8060
8061     if (IS_ANIMATED(graphic))
8062       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8063
8064     return;
8065   }
8066
8067   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8068     return;
8069
8070   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8071
8072   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8073 }
8074
8075 void CheckExitSP(int x, int y)
8076 {
8077   if (local_player->gems_still_needed > 0)
8078   {
8079     int element = Feld[x][y];
8080     int graphic = el2img(element);
8081
8082     if (IS_ANIMATED(graphic))
8083       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8084
8085     return;
8086   }
8087
8088   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8089     return;
8090
8091   Feld[x][y] = EL_SP_EXIT_OPENING;
8092
8093   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8094 }
8095
8096 static void CloseAllOpenTimegates()
8097 {
8098   int x, y;
8099
8100   SCAN_PLAYFIELD(x, y)
8101   {
8102     int element = Feld[x][y];
8103
8104     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8105     {
8106       Feld[x][y] = EL_TIMEGATE_CLOSING;
8107
8108       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8109     }
8110   }
8111 }
8112
8113 void DrawTwinkleOnField(int x, int y)
8114 {
8115   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8116     return;
8117
8118   if (Feld[x][y] == EL_BD_DIAMOND)
8119     return;
8120
8121   if (MovDelay[x][y] == 0)      /* next animation frame */
8122     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8123
8124   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8125   {
8126     MovDelay[x][y]--;
8127
8128     if (setup.direct_draw && MovDelay[x][y])
8129       SetDrawtoField(DRAW_BUFFERED);
8130
8131     DrawLevelElementAnimation(x, y, Feld[x][y]);
8132
8133     if (MovDelay[x][y] != 0)
8134     {
8135       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8136                                            10 - MovDelay[x][y]);
8137
8138       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8139
8140       if (setup.direct_draw)
8141       {
8142         int dest_x, dest_y;
8143
8144         dest_x = FX + SCREENX(x) * TILEX;
8145         dest_y = FY + SCREENY(y) * TILEY;
8146
8147         BlitBitmap(drawto_field, window,
8148                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8149         SetDrawtoField(DRAW_DIRECT);
8150       }
8151     }
8152   }
8153 }
8154
8155 void MauerWaechst(int x, int y)
8156 {
8157   int delay = 6;
8158
8159   if (!MovDelay[x][y])          /* next animation frame */
8160     MovDelay[x][y] = 3 * delay;
8161
8162   if (MovDelay[x][y])           /* wait some time before next frame */
8163   {
8164     MovDelay[x][y]--;
8165
8166     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8167     {
8168       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8169       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8170
8171       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8172     }
8173
8174     if (!MovDelay[x][y])
8175     {
8176       if (MovDir[x][y] == MV_LEFT)
8177       {
8178         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8179           DrawLevelField(x - 1, y);
8180       }
8181       else if (MovDir[x][y] == MV_RIGHT)
8182       {
8183         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8184           DrawLevelField(x + 1, y);
8185       }
8186       else if (MovDir[x][y] == MV_UP)
8187       {
8188         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8189           DrawLevelField(x, y - 1);
8190       }
8191       else
8192       {
8193         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8194           DrawLevelField(x, y + 1);
8195       }
8196
8197       Feld[x][y] = Store[x][y];
8198       Store[x][y] = 0;
8199       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8200       DrawLevelField(x, y);
8201     }
8202   }
8203 }
8204
8205 void MauerAbleger(int ax, int ay)
8206 {
8207   int element = Feld[ax][ay];
8208   int graphic = el2img(element);
8209   boolean oben_frei = FALSE, unten_frei = FALSE;
8210   boolean links_frei = FALSE, rechts_frei = FALSE;
8211   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8212   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8213   boolean new_wall = FALSE;
8214
8215   if (IS_ANIMATED(graphic))
8216     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8217
8218   if (!MovDelay[ax][ay])        /* start building new wall */
8219     MovDelay[ax][ay] = 6;
8220
8221   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8222   {
8223     MovDelay[ax][ay]--;
8224     if (MovDelay[ax][ay])
8225       return;
8226   }
8227
8228   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8229     oben_frei = TRUE;
8230   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8231     unten_frei = TRUE;
8232   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8233     links_frei = TRUE;
8234   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8235     rechts_frei = TRUE;
8236
8237   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8238       element == EL_EXPANDABLE_WALL_ANY)
8239   {
8240     if (oben_frei)
8241     {
8242       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8243       Store[ax][ay-1] = element;
8244       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8245       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8246         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8247                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8248       new_wall = TRUE;
8249     }
8250     if (unten_frei)
8251     {
8252       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8253       Store[ax][ay+1] = element;
8254       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8255       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8256         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8257                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8258       new_wall = TRUE;
8259     }
8260   }
8261
8262   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8263       element == EL_EXPANDABLE_WALL_ANY ||
8264       element == EL_EXPANDABLE_WALL ||
8265       element == EL_BD_EXPANDABLE_WALL)
8266   {
8267     if (links_frei)
8268     {
8269       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8270       Store[ax-1][ay] = element;
8271       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8272       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8273         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8274                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8275       new_wall = TRUE;
8276     }
8277
8278     if (rechts_frei)
8279     {
8280       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8281       Store[ax+1][ay] = element;
8282       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8283       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8284         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8285                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8286       new_wall = TRUE;
8287     }
8288   }
8289
8290   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8291     DrawLevelField(ax, ay);
8292
8293   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8294     oben_massiv = TRUE;
8295   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8296     unten_massiv = TRUE;
8297   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8298     links_massiv = TRUE;
8299   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8300     rechts_massiv = TRUE;
8301
8302   if (((oben_massiv && unten_massiv) ||
8303        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8304        element == EL_EXPANDABLE_WALL) &&
8305       ((links_massiv && rechts_massiv) ||
8306        element == EL_EXPANDABLE_WALL_VERTICAL))
8307     Feld[ax][ay] = EL_WALL;
8308
8309   if (new_wall)
8310     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8311 }
8312
8313 void MauerAblegerStahl(int ax, int ay)
8314 {
8315   int element = Feld[ax][ay];
8316   int graphic = el2img(element);
8317   boolean oben_frei = FALSE, unten_frei = FALSE;
8318   boolean links_frei = FALSE, rechts_frei = FALSE;
8319   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8320   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8321   boolean new_wall = FALSE;
8322
8323   if (IS_ANIMATED(graphic))
8324     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8325
8326   if (!MovDelay[ax][ay])        /* start building new wall */
8327     MovDelay[ax][ay] = 6;
8328
8329   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8330   {
8331     MovDelay[ax][ay]--;
8332     if (MovDelay[ax][ay])
8333       return;
8334   }
8335
8336   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8337     oben_frei = TRUE;
8338   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8339     unten_frei = TRUE;
8340   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8341     links_frei = TRUE;
8342   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8343     rechts_frei = TRUE;
8344
8345   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8346       element == EL_EXPANDABLE_STEELWALL_ANY)
8347   {
8348     if (oben_frei)
8349     {
8350       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8351       Store[ax][ay-1] = element;
8352       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8353       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8354         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8355                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8356       new_wall = TRUE;
8357     }
8358     if (unten_frei)
8359     {
8360       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8361       Store[ax][ay+1] = element;
8362       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8363       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8364         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8365                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8366       new_wall = TRUE;
8367     }
8368   }
8369
8370   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8371       element == EL_EXPANDABLE_STEELWALL_ANY)
8372   {
8373     if (links_frei)
8374     {
8375       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8376       Store[ax-1][ay] = element;
8377       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8378       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8379         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8380                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8381       new_wall = TRUE;
8382     }
8383
8384     if (rechts_frei)
8385     {
8386       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8387       Store[ax+1][ay] = element;
8388       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8389       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8390         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8391                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8392       new_wall = TRUE;
8393     }
8394   }
8395
8396   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8397     oben_massiv = TRUE;
8398   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8399     unten_massiv = TRUE;
8400   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8401     links_massiv = TRUE;
8402   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8403     rechts_massiv = TRUE;
8404
8405   if (((oben_massiv && unten_massiv) ||
8406        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8407       ((links_massiv && rechts_massiv) ||
8408        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8409     Feld[ax][ay] = EL_WALL;
8410
8411   if (new_wall)
8412     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8413 }
8414
8415 void CheckForDragon(int x, int y)
8416 {
8417   int i, j;
8418   boolean dragon_found = FALSE;
8419   static int xy[4][2] =
8420   {
8421     { 0, -1 },
8422     { -1, 0 },
8423     { +1, 0 },
8424     { 0, +1 }
8425   };
8426
8427   for (i = 0; i < NUM_DIRECTIONS; i++)
8428   {
8429     for (j = 0; j < 4; j++)
8430     {
8431       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8432
8433       if (IN_LEV_FIELD(xx, yy) &&
8434           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8435       {
8436         if (Feld[xx][yy] == EL_DRAGON)
8437           dragon_found = TRUE;
8438       }
8439       else
8440         break;
8441     }
8442   }
8443
8444   if (!dragon_found)
8445   {
8446     for (i = 0; i < NUM_DIRECTIONS; i++)
8447     {
8448       for (j = 0; j < 3; j++)
8449       {
8450         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8451   
8452         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8453         {
8454           Feld[xx][yy] = EL_EMPTY;
8455           DrawLevelField(xx, yy);
8456         }
8457         else
8458           break;
8459       }
8460     }
8461   }
8462 }
8463
8464 static void InitBuggyBase(int x, int y)
8465 {
8466   int element = Feld[x][y];
8467   int activating_delay = FRAMES_PER_SECOND / 4;
8468
8469   ChangeDelay[x][y] =
8470     (element == EL_SP_BUGGY_BASE ?
8471      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8472      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8473      activating_delay :
8474      element == EL_SP_BUGGY_BASE_ACTIVE ?
8475      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8476 }
8477
8478 static void WarnBuggyBase(int x, int y)
8479 {
8480   int i;
8481   static int xy[4][2] =
8482   {
8483     { 0, -1 },
8484     { -1, 0 },
8485     { +1, 0 },
8486     { 0, +1 }
8487   };
8488
8489   for (i = 0; i < NUM_DIRECTIONS; i++)
8490   {
8491     int xx = x + xy[i][0];
8492     int yy = y + xy[i][1];
8493
8494     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8495     {
8496       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8497
8498       break;
8499     }
8500   }
8501 }
8502
8503 static void InitTrap(int x, int y)
8504 {
8505   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8506 }
8507
8508 static void ActivateTrap(int x, int y)
8509 {
8510   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8511 }
8512
8513 static void ChangeActiveTrap(int x, int y)
8514 {
8515   int graphic = IMG_TRAP_ACTIVE;
8516
8517   /* if new animation frame was drawn, correct crumbled sand border */
8518   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8519     DrawLevelFieldCrumbledSand(x, y);
8520 }
8521
8522 static int getSpecialActionElement(int element, int number, int base_element)
8523 {
8524   return (element != EL_EMPTY ? element :
8525           number != -1 ? base_element + number - 1 :
8526           EL_EMPTY);
8527 }
8528
8529 static int getModifiedActionNumber(int value_old, int operator, int operand,
8530                                    int value_min, int value_max)
8531 {
8532   int value_new = (operator == CA_MODE_SET      ? operand :
8533                    operator == CA_MODE_ADD      ? value_old + operand :
8534                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8535                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8536                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8537                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8538                    value_old);
8539
8540   return (value_new < value_min ? value_min :
8541           value_new > value_max ? value_max :
8542           value_new);
8543 }
8544
8545 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8546 {
8547   struct ElementInfo *ei = &element_info[element];
8548   struct ElementChangeInfo *change = &ei->change_page[page];
8549   int target_element = change->target_element;
8550   int action_type = change->action_type;
8551   int action_mode = change->action_mode;
8552   int action_arg = change->action_arg;
8553   int i;
8554
8555   if (!change->has_action)
8556     return;
8557
8558   /* ---------- determine action paramater values -------------------------- */
8559
8560   int level_time_value =
8561     (level.time > 0 ? TimeLeft :
8562      TimePlayed);
8563
8564   int action_arg_element =
8565     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8566      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8567      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8568      EL_EMPTY);
8569
8570   int action_arg_direction =
8571     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8572      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8573      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8574      change->actual_trigger_side :
8575      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8576      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8577      MV_NONE);
8578
8579   int action_arg_number_min =
8580     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8581      CA_ARG_MIN);
8582
8583   int action_arg_number_max =
8584     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8585      action_type == CA_SET_LEVEL_GEMS ? 999 :
8586      action_type == CA_SET_LEVEL_TIME ? 9999 :
8587      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8588      action_type == CA_SET_CE_VALUE ? 9999 :
8589      action_type == CA_SET_CE_SCORE ? 9999 :
8590      CA_ARG_MAX);
8591
8592   int action_arg_number_reset =
8593     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8594      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8595      action_type == CA_SET_LEVEL_TIME ? level.time :
8596      action_type == CA_SET_LEVEL_SCORE ? 0 :
8597 #if USE_NEW_CUSTOM_VALUE
8598      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8599 #else
8600      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8601 #endif
8602      action_type == CA_SET_CE_SCORE ? 0 :
8603      0);
8604
8605   int action_arg_number =
8606     (action_arg <= CA_ARG_MAX ? action_arg :
8607      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8608      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8609      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8610      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8611      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8612      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8613 #if USE_NEW_CUSTOM_VALUE
8614      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8615 #else
8616      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8617 #endif
8618      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8619      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8620      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8621      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8622      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8623      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8624      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8625      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8626      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8627      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8628      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8629      -1);
8630
8631   int action_arg_number_old =
8632     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8633      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8634      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8635      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8636      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8637      0);
8638
8639   int action_arg_number_new =
8640     getModifiedActionNumber(action_arg_number_old,
8641                             action_mode, action_arg_number,
8642                             action_arg_number_min, action_arg_number_max);
8643
8644   int trigger_player_bits =
8645     (change->actual_trigger_player >= EL_PLAYER_1 &&
8646      change->actual_trigger_player <= EL_PLAYER_4 ?
8647      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8648      PLAYER_BITS_ANY);
8649
8650   int action_arg_player_bits =
8651     (action_arg >= CA_ARG_PLAYER_1 &&
8652      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8653      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8654      PLAYER_BITS_ANY);
8655
8656   /* ---------- execute action  -------------------------------------------- */
8657
8658   switch (action_type)
8659   {
8660     case CA_NO_ACTION:
8661     {
8662       return;
8663     }
8664
8665     /* ---------- level actions  ------------------------------------------- */
8666
8667     case CA_RESTART_LEVEL:
8668     {
8669       game.restart_level = TRUE;
8670
8671       break;
8672     }
8673
8674     case CA_SHOW_ENVELOPE:
8675     {
8676       int element = getSpecialActionElement(action_arg_element,
8677                                             action_arg_number, EL_ENVELOPE_1);
8678
8679       if (IS_ENVELOPE(element))
8680         local_player->show_envelope = element;
8681
8682       break;
8683     }
8684
8685     case CA_SET_LEVEL_TIME:
8686     {
8687       if (level.time > 0)       /* only modify limited time value */
8688       {
8689         TimeLeft = action_arg_number_new;
8690
8691         DrawGameValue_Time(TimeLeft);
8692
8693         if (!TimeLeft && setup.time_limit)
8694           for (i = 0; i < MAX_PLAYERS; i++)
8695             KillPlayer(&stored_player[i]);
8696       }
8697
8698       break;
8699     }
8700
8701     case CA_SET_LEVEL_SCORE:
8702     {
8703       local_player->score = action_arg_number_new;
8704
8705       DrawGameValue_Score(local_player->score);
8706
8707       break;
8708     }
8709
8710     case CA_SET_LEVEL_GEMS:
8711     {
8712       local_player->gems_still_needed = action_arg_number_new;
8713
8714       DrawGameValue_Emeralds(local_player->gems_still_needed);
8715
8716       break;
8717     }
8718
8719 #if !USE_PLAYER_GRAVITY
8720     case CA_SET_LEVEL_GRAVITY:
8721     {
8722       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8723                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8724                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8725                       game.gravity);
8726       break;
8727     }
8728 #endif
8729
8730     case CA_SET_LEVEL_WIND:
8731     {
8732       game.wind_direction = action_arg_direction;
8733
8734       break;
8735     }
8736
8737     /* ---------- player actions  ------------------------------------------ */
8738
8739     case CA_MOVE_PLAYER:
8740     {
8741       /* automatically move to the next field in specified direction */
8742       for (i = 0; i < MAX_PLAYERS; i++)
8743         if (trigger_player_bits & (1 << i))
8744           stored_player[i].programmed_action = action_arg_direction;
8745
8746       break;
8747     }
8748
8749     case CA_EXIT_PLAYER:
8750     {
8751       for (i = 0; i < MAX_PLAYERS; i++)
8752         if (action_arg_player_bits & (1 << i))
8753           PlayerWins(&stored_player[i]);
8754
8755       break;
8756     }
8757
8758     case CA_KILL_PLAYER:
8759     {
8760       for (i = 0; i < MAX_PLAYERS; i++)
8761         if (action_arg_player_bits & (1 << i))
8762           KillPlayer(&stored_player[i]);
8763
8764       break;
8765     }
8766
8767     case CA_SET_PLAYER_KEYS:
8768     {
8769       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8770       int element = getSpecialActionElement(action_arg_element,
8771                                             action_arg_number, EL_KEY_1);
8772
8773       if (IS_KEY(element))
8774       {
8775         for (i = 0; i < MAX_PLAYERS; i++)
8776         {
8777           if (trigger_player_bits & (1 << i))
8778           {
8779             stored_player[i].key[KEY_NR(element)] = key_state;
8780
8781             DrawGameDoorValues();
8782           }
8783         }
8784       }
8785
8786       break;
8787     }
8788
8789     case CA_SET_PLAYER_SPEED:
8790     {
8791       for (i = 0; i < MAX_PLAYERS; i++)
8792       {
8793         if (trigger_player_bits & (1 << i))
8794         {
8795           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8796
8797           if (action_arg == CA_ARG_SPEED_FASTER &&
8798               stored_player[i].cannot_move)
8799           {
8800             action_arg_number = STEPSIZE_VERY_SLOW;
8801           }
8802           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8803                    action_arg == CA_ARG_SPEED_FASTER)
8804           {
8805             action_arg_number = 2;
8806             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8807                            CA_MODE_MULTIPLY);
8808           }
8809           else if (action_arg == CA_ARG_NUMBER_RESET)
8810           {
8811             action_arg_number = level.initial_player_stepsize[i];
8812           }
8813
8814           move_stepsize =
8815             getModifiedActionNumber(move_stepsize,
8816                                     action_mode,
8817                                     action_arg_number,
8818                                     action_arg_number_min,
8819                                     action_arg_number_max);
8820
8821           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8822         }
8823       }
8824
8825       break;
8826     }
8827
8828     case CA_SET_PLAYER_SHIELD:
8829     {
8830       for (i = 0; i < MAX_PLAYERS; i++)
8831       {
8832         if (trigger_player_bits & (1 << i))
8833         {
8834           if (action_arg == CA_ARG_SHIELD_OFF)
8835           {
8836             stored_player[i].shield_normal_time_left = 0;
8837             stored_player[i].shield_deadly_time_left = 0;
8838           }
8839           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8840           {
8841             stored_player[i].shield_normal_time_left = 999999;
8842           }
8843           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8844           {
8845             stored_player[i].shield_normal_time_left = 999999;
8846             stored_player[i].shield_deadly_time_left = 999999;
8847           }
8848         }
8849       }
8850
8851       break;
8852     }
8853
8854 #if USE_PLAYER_GRAVITY
8855     case CA_SET_PLAYER_GRAVITY:
8856     {
8857       for (i = 0; i < MAX_PLAYERS; i++)
8858       {
8859         if (trigger_player_bits & (1 << i))
8860         {
8861           stored_player[i].gravity =
8862             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8863              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8864              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8865              stored_player[i].gravity);
8866         }
8867       }
8868
8869       break;
8870     }
8871 #endif
8872
8873     case CA_SET_PLAYER_ARTWORK:
8874     {
8875       for (i = 0; i < MAX_PLAYERS; i++)
8876       {
8877         if (trigger_player_bits & (1 << i))
8878         {
8879           int artwork_element = action_arg_element;
8880
8881           if (action_arg == CA_ARG_ELEMENT_RESET)
8882             artwork_element =
8883               (level.use_artwork_element[i] ? level.artwork_element[i] :
8884                stored_player[i].element_nr);
8885
8886 #if USE_GFX_RESET_PLAYER_ARTWORK
8887           if (stored_player[i].artwork_element != artwork_element)
8888             stored_player[i].Frame = 0;
8889 #endif
8890
8891           stored_player[i].artwork_element = artwork_element;
8892
8893           SetPlayerWaiting(&stored_player[i], FALSE);
8894
8895           /* set number of special actions for bored and sleeping animation */
8896           stored_player[i].num_special_action_bored =
8897             get_num_special_action(artwork_element,
8898                                    ACTION_BORING_1, ACTION_BORING_LAST);
8899           stored_player[i].num_special_action_sleeping =
8900             get_num_special_action(artwork_element,
8901                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8902         }
8903       }
8904
8905       break;
8906     }
8907
8908     /* ---------- CE actions  ---------------------------------------------- */
8909
8910     case CA_SET_CE_VALUE:
8911     {
8912 #if USE_NEW_CUSTOM_VALUE
8913       int last_ce_value = CustomValue[x][y];
8914
8915       CustomValue[x][y] = action_arg_number_new;
8916
8917       if (CustomValue[x][y] != last_ce_value)
8918       {
8919         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8920         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8921
8922         if (CustomValue[x][y] == 0)
8923         {
8924           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8925           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8926         }
8927       }
8928 #endif
8929
8930       break;
8931     }
8932
8933     case CA_SET_CE_SCORE:
8934     {
8935 #if USE_NEW_CUSTOM_VALUE
8936       int last_ce_score = ei->collect_score;
8937
8938       ei->collect_score = action_arg_number_new;
8939
8940       if (ei->collect_score != last_ce_score)
8941       {
8942         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8943         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8944
8945         if (ei->collect_score == 0)
8946         {
8947           int xx, yy;
8948
8949           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8950           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8951
8952           /*
8953             This is a very special case that seems to be a mixture between
8954             CheckElementChange() and CheckTriggeredElementChange(): while
8955             the first one only affects single elements that are triggered
8956             directly, the second one affects multiple elements in the playfield
8957             that are triggered indirectly by another element. This is a third
8958             case: Changing the CE score always affects multiple identical CEs,
8959             so every affected CE must be checked, not only the single CE for
8960             which the CE score was changed in the first place (as every instance
8961             of that CE shares the same CE score, and therefore also can change)!
8962           */
8963           SCAN_PLAYFIELD(xx, yy)
8964           {
8965             if (Feld[xx][yy] == element)
8966               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8967                                  CE_SCORE_GETS_ZERO);
8968           }
8969         }
8970       }
8971 #endif
8972
8973       break;
8974     }
8975
8976     /* ---------- engine actions  ------------------------------------------ */
8977
8978     case CA_SET_ENGINE_SCAN_MODE:
8979     {
8980       InitPlayfieldScanMode(action_arg);
8981
8982       break;
8983     }
8984
8985     default:
8986       break;
8987   }
8988 }
8989
8990 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8991 {
8992   int old_element = Feld[x][y];
8993   int new_element = GetElementFromGroupElement(element);
8994   int previous_move_direction = MovDir[x][y];
8995 #if USE_NEW_CUSTOM_VALUE
8996   int last_ce_value = CustomValue[x][y];
8997 #endif
8998   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8999   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9000   boolean add_player_onto_element = (new_element_is_player &&
9001 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9002                                      /* this breaks SnakeBite when a snake is
9003                                         halfway through a door that closes */
9004                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9005                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9006 #endif
9007                                      IS_WALKABLE(old_element));
9008
9009 #if 0
9010   /* check if element under the player changes from accessible to unaccessible
9011      (needed for special case of dropping element which then changes) */
9012   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9013       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9014   {
9015     Bang(x, y);
9016
9017     return;
9018   }
9019 #endif
9020
9021   if (!add_player_onto_element)
9022   {
9023     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9024       RemoveMovingField(x, y);
9025     else
9026       RemoveField(x, y);
9027
9028     Feld[x][y] = new_element;
9029
9030 #if !USE_GFX_RESET_GFX_ANIMATION
9031     ResetGfxAnimation(x, y);
9032     ResetRandomAnimationValue(x, y);
9033 #endif
9034
9035     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9036       MovDir[x][y] = previous_move_direction;
9037
9038 #if USE_NEW_CUSTOM_VALUE
9039     if (element_info[new_element].use_last_ce_value)
9040       CustomValue[x][y] = last_ce_value;
9041 #endif
9042
9043     InitField_WithBug1(x, y, FALSE);
9044
9045     new_element = Feld[x][y];   /* element may have changed */
9046
9047 #if USE_GFX_RESET_GFX_ANIMATION
9048     ResetGfxAnimation(x, y);
9049     ResetRandomAnimationValue(x, y);
9050 #endif
9051
9052     DrawLevelField(x, y);
9053
9054     if (GFX_CRUMBLED(new_element))
9055       DrawLevelFieldCrumbledSandNeighbours(x, y);
9056   }
9057
9058 #if 1
9059   /* check if element under the player changes from accessible to unaccessible
9060      (needed for special case of dropping element which then changes) */
9061   /* (must be checked after creating new element for walkable group elements) */
9062 #if USE_FIX_KILLED_BY_NON_WALKABLE
9063   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9064       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9065   {
9066     Bang(x, y);
9067
9068     return;
9069   }
9070 #else
9071   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9072       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9073   {
9074     Bang(x, y);
9075
9076     return;
9077   }
9078 #endif
9079 #endif
9080
9081   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9082   if (new_element_is_player)
9083     RelocatePlayer(x, y, new_element);
9084
9085   if (is_change)
9086     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9087
9088   TestIfBadThingTouchesPlayer(x, y);
9089   TestIfPlayerTouchesCustomElement(x, y);
9090   TestIfElementTouchesCustomElement(x, y);
9091 }
9092
9093 static void CreateField(int x, int y, int element)
9094 {
9095   CreateFieldExt(x, y, element, FALSE);
9096 }
9097
9098 static void CreateElementFromChange(int x, int y, int element)
9099 {
9100   element = GET_VALID_RUNTIME_ELEMENT(element);
9101
9102 #if USE_STOP_CHANGED_ELEMENTS
9103   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9104   {
9105     int old_element = Feld[x][y];
9106
9107     /* prevent changed element from moving in same engine frame
9108        unless both old and new element can either fall or move */
9109     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9110         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9111       Stop[x][y] = TRUE;
9112   }
9113 #endif
9114
9115   CreateFieldExt(x, y, element, TRUE);
9116 }
9117
9118 static boolean ChangeElement(int x, int y, int element, int page)
9119 {
9120   struct ElementInfo *ei = &element_info[element];
9121   struct ElementChangeInfo *change = &ei->change_page[page];
9122   int ce_value = CustomValue[x][y];
9123   int ce_score = ei->collect_score;
9124   int target_element;
9125   int old_element = Feld[x][y];
9126
9127   /* always use default change event to prevent running into a loop */
9128   if (ChangeEvent[x][y] == -1)
9129     ChangeEvent[x][y] = CE_DELAY;
9130
9131   if (ChangeEvent[x][y] == CE_DELAY)
9132   {
9133     /* reset actual trigger element, trigger player and action element */
9134     change->actual_trigger_element = EL_EMPTY;
9135     change->actual_trigger_player = EL_PLAYER_1;
9136     change->actual_trigger_side = CH_SIDE_NONE;
9137     change->actual_trigger_ce_value = 0;
9138     change->actual_trigger_ce_score = 0;
9139   }
9140
9141   /* do not change elements more than a specified maximum number of changes */
9142   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
9143     return FALSE;
9144
9145   ChangeCount[x][y]++;          /* count number of changes in the same frame */
9146
9147   if (change->explode)
9148   {
9149     Bang(x, y);
9150
9151     return TRUE;
9152   }
9153
9154   if (change->use_target_content)
9155   {
9156     boolean complete_replace = TRUE;
9157     boolean can_replace[3][3];
9158     int xx, yy;
9159
9160     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9161     {
9162       boolean is_empty;
9163       boolean is_walkable;
9164       boolean is_diggable;
9165       boolean is_collectible;
9166       boolean is_removable;
9167       boolean is_destructible;
9168       int ex = x + xx - 1;
9169       int ey = y + yy - 1;
9170       int content_element = change->target_content.e[xx][yy];
9171       int e;
9172
9173       can_replace[xx][yy] = TRUE;
9174
9175       if (ex == x && ey == y)   /* do not check changing element itself */
9176         continue;
9177
9178       if (content_element == EL_EMPTY_SPACE)
9179       {
9180         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
9181
9182         continue;
9183       }
9184
9185       if (!IN_LEV_FIELD(ex, ey))
9186       {
9187         can_replace[xx][yy] = FALSE;
9188         complete_replace = FALSE;
9189
9190         continue;
9191       }
9192
9193       e = Feld[ex][ey];
9194
9195       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9196         e = MovingOrBlocked2Element(ex, ey);
9197
9198       is_empty = (IS_FREE(ex, ey) ||
9199                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
9200
9201       is_walkable     = (is_empty || IS_WALKABLE(e));
9202       is_diggable     = (is_empty || IS_DIGGABLE(e));
9203       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
9204       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
9205       is_removable    = (is_diggable || is_collectible);
9206
9207       can_replace[xx][yy] =
9208         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9209           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9210           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9211           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9212           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9213           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9214          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9215
9216       if (!can_replace[xx][yy])
9217         complete_replace = FALSE;
9218     }
9219
9220     if (!change->only_if_complete || complete_replace)
9221     {
9222       boolean something_has_changed = FALSE;
9223
9224       if (change->only_if_complete && change->use_random_replace &&
9225           RND(100) < change->random_percentage)
9226         return FALSE;
9227
9228       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9229       {
9230         int ex = x + xx - 1;
9231         int ey = y + yy - 1;
9232         int content_element;
9233
9234         if (can_replace[xx][yy] && (!change->use_random_replace ||
9235                                     RND(100) < change->random_percentage))
9236         {
9237           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9238             RemoveMovingField(ex, ey);
9239
9240           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9241
9242           content_element = change->target_content.e[xx][yy];
9243           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9244                                               ce_value, ce_score);
9245
9246           CreateElementFromChange(ex, ey, target_element);
9247
9248           something_has_changed = TRUE;
9249
9250           /* for symmetry reasons, freeze newly created border elements */
9251           if (ex != x || ey != y)
9252             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9253         }
9254       }
9255
9256       if (something_has_changed)
9257       {
9258         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9259         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9260       }
9261     }
9262   }
9263   else
9264   {
9265     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9266                                         ce_value, ce_score);
9267
9268     if (element == EL_DIAGONAL_GROWING ||
9269         element == EL_DIAGONAL_SHRINKING)
9270     {
9271       target_element = Store[x][y];
9272
9273       Store[x][y] = EL_EMPTY;
9274     }
9275
9276     CreateElementFromChange(x, y, target_element);
9277
9278     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9279     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9280   }
9281
9282   /* this uses direct change before indirect change */
9283   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9284
9285   return TRUE;
9286 }
9287
9288 #if USE_NEW_DELAYED_ACTION
9289
9290 static void HandleElementChange(int x, int y, int page)
9291 {
9292   int element = MovingOrBlocked2Element(x, y);
9293   struct ElementInfo *ei = &element_info[element];
9294   struct ElementChangeInfo *change = &ei->change_page[page];
9295
9296 #ifdef DEBUG
9297   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9298       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9299   {
9300     printf("\n\n");
9301     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9302            x, y, element, element_info[element].token_name);
9303     printf("HandleElementChange(): This should never happen!\n");
9304     printf("\n\n");
9305   }
9306 #endif
9307
9308   /* this can happen with classic bombs on walkable, changing elements */
9309   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9310   {
9311 #if 0
9312     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9313       ChangeDelay[x][y] = 0;
9314 #endif
9315
9316     return;
9317   }
9318
9319   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9320   {
9321     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9322
9323     if (change->can_change)
9324     {
9325 #if 1
9326       /* !!! not clear why graphic animation should be reset at all here !!! */
9327       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9328 #if USE_GFX_RESET_WHEN_NOT_MOVING
9329       /* when a custom element is about to change (for example by change delay),
9330          do not reset graphic animation when the custom element is moving */
9331       if (!IS_MOVING(x, y))
9332 #endif
9333       {
9334         ResetGfxAnimation(x, y);
9335         ResetRandomAnimationValue(x, y);
9336       }
9337 #endif
9338
9339       if (change->pre_change_function)
9340         change->pre_change_function(x, y);
9341     }
9342   }
9343
9344   ChangeDelay[x][y]--;
9345
9346   if (ChangeDelay[x][y] != 0)           /* continue element change */
9347   {
9348     if (change->can_change)
9349     {
9350       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9351
9352       if (IS_ANIMATED(graphic))
9353         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9354
9355       if (change->change_function)
9356         change->change_function(x, y);
9357     }
9358   }
9359   else                                  /* finish element change */
9360   {
9361     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9362     {
9363       page = ChangePage[x][y];
9364       ChangePage[x][y] = -1;
9365
9366       change = &ei->change_page[page];
9367     }
9368
9369     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9370     {
9371       ChangeDelay[x][y] = 1;            /* try change after next move step */
9372       ChangePage[x][y] = page;          /* remember page to use for change */
9373
9374       return;
9375     }
9376
9377     if (change->can_change)
9378     {
9379       if (ChangeElement(x, y, element, page))
9380       {
9381         if (change->post_change_function)
9382           change->post_change_function(x, y);
9383       }
9384     }
9385
9386     if (change->has_action)
9387       ExecuteCustomElementAction(x, y, element, page);
9388   }
9389 }
9390
9391 #else
9392
9393 static void HandleElementChange(int x, int y, int page)
9394 {
9395   int element = MovingOrBlocked2Element(x, y);
9396   struct ElementInfo *ei = &element_info[element];
9397   struct ElementChangeInfo *change = &ei->change_page[page];
9398
9399 #ifdef DEBUG
9400   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9401   {
9402     printf("\n\n");
9403     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9404            x, y, element, element_info[element].token_name);
9405     printf("HandleElementChange(): This should never happen!\n");
9406     printf("\n\n");
9407   }
9408 #endif
9409
9410   /* this can happen with classic bombs on walkable, changing elements */
9411   if (!CAN_CHANGE(element))
9412   {
9413 #if 0
9414     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9415       ChangeDelay[x][y] = 0;
9416 #endif
9417
9418     return;
9419   }
9420
9421   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9422   {
9423     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9424
9425     ResetGfxAnimation(x, y);
9426     ResetRandomAnimationValue(x, y);
9427
9428     if (change->pre_change_function)
9429       change->pre_change_function(x, y);
9430   }
9431
9432   ChangeDelay[x][y]--;
9433
9434   if (ChangeDelay[x][y] != 0)           /* continue element change */
9435   {
9436     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9437
9438     if (IS_ANIMATED(graphic))
9439       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9440
9441     if (change->change_function)
9442       change->change_function(x, y);
9443   }
9444   else                                  /* finish element change */
9445   {
9446     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9447     {
9448       page = ChangePage[x][y];
9449       ChangePage[x][y] = -1;
9450
9451       change = &ei->change_page[page];
9452     }
9453
9454     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9455     {
9456       ChangeDelay[x][y] = 1;            /* try change after next move step */
9457       ChangePage[x][y] = page;          /* remember page to use for change */
9458
9459       return;
9460     }
9461
9462     if (ChangeElement(x, y, element, page))
9463     {
9464       if (change->post_change_function)
9465         change->post_change_function(x, y);
9466     }
9467   }
9468 }
9469
9470 #endif
9471
9472 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9473                                               int trigger_element,
9474                                               int trigger_event,
9475                                               int trigger_player,
9476                                               int trigger_side,
9477                                               int trigger_page)
9478 {
9479   boolean change_done_any = FALSE;
9480   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9481   int i;
9482
9483   if (!(trigger_events[trigger_element][trigger_event]))
9484     return FALSE;
9485
9486 #if 0
9487   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9488          trigger_event, recursion_loop_depth, recursion_loop_detected,
9489          recursion_loop_element, EL_NAME(recursion_loop_element));
9490 #endif
9491
9492   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9493
9494   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9495   {
9496     int element = EL_CUSTOM_START + i;
9497     boolean change_done = FALSE;
9498     int p;
9499
9500     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9501         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9502       continue;
9503
9504     for (p = 0; p < element_info[element].num_change_pages; p++)
9505     {
9506       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9507
9508       if (change->can_change_or_has_action &&
9509           change->has_event[trigger_event] &&
9510           change->trigger_side & trigger_side &&
9511           change->trigger_player & trigger_player &&
9512           change->trigger_page & trigger_page_bits &&
9513           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9514       {
9515         change->actual_trigger_element = trigger_element;
9516         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9517         change->actual_trigger_side = trigger_side;
9518         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9519         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9520
9521         if ((change->can_change && !change_done) || change->has_action)
9522         {
9523           int x, y;
9524
9525           SCAN_PLAYFIELD(x, y)
9526           {
9527             if (Feld[x][y] == element)
9528             {
9529               if (change->can_change && !change_done)
9530               {
9531                 ChangeDelay[x][y] = 1;
9532                 ChangeEvent[x][y] = trigger_event;
9533
9534                 HandleElementChange(x, y, p);
9535               }
9536 #if USE_NEW_DELAYED_ACTION
9537               else if (change->has_action)
9538               {
9539                 ExecuteCustomElementAction(x, y, element, p);
9540                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9541               }
9542 #else
9543               if (change->has_action)
9544               {
9545                 ExecuteCustomElementAction(x, y, element, p);
9546                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9547               }
9548 #endif
9549             }
9550           }
9551
9552           if (change->can_change)
9553           {
9554             change_done = TRUE;
9555             change_done_any = TRUE;
9556           }
9557         }
9558       }
9559     }
9560   }
9561
9562   RECURSION_LOOP_DETECTION_END();
9563
9564   return change_done_any;
9565 }
9566
9567 static boolean CheckElementChangeExt(int x, int y,
9568                                      int element,
9569                                      int trigger_element,
9570                                      int trigger_event,
9571                                      int trigger_player,
9572                                      int trigger_side)
9573 {
9574   boolean change_done = FALSE;
9575   int p;
9576
9577   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9578       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9579     return FALSE;
9580
9581   if (Feld[x][y] == EL_BLOCKED)
9582   {
9583     Blocked2Moving(x, y, &x, &y);
9584     element = Feld[x][y];
9585   }
9586
9587 #if 0
9588   /* check if element has already changed */
9589   if (Feld[x][y] != element)
9590     return FALSE;
9591 #else
9592   /* check if element has already changed or is about to change after moving */
9593   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9594        Feld[x][y] != element) ||
9595
9596       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9597        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9598         ChangePage[x][y] != -1)))
9599     return FALSE;
9600 #endif
9601
9602 #if 0
9603   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9604          trigger_event, recursion_loop_depth, recursion_loop_detected,
9605          recursion_loop_element, EL_NAME(recursion_loop_element));
9606 #endif
9607
9608   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9609
9610   for (p = 0; p < element_info[element].num_change_pages; p++)
9611   {
9612     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9613
9614     /* check trigger element for all events where the element that is checked
9615        for changing interacts with a directly adjacent element -- this is
9616        different to element changes that affect other elements to change on the
9617        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9618     boolean check_trigger_element =
9619       (trigger_event == CE_TOUCHING_X ||
9620        trigger_event == CE_HITTING_X ||
9621        trigger_event == CE_HIT_BY_X ||
9622 #if 1
9623        /* this one was forgotten until 3.2.3 */
9624        trigger_event == CE_DIGGING_X);
9625 #endif
9626
9627     if (change->can_change_or_has_action &&
9628         change->has_event[trigger_event] &&
9629         change->trigger_side & trigger_side &&
9630         change->trigger_player & trigger_player &&
9631         (!check_trigger_element ||
9632          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9633     {
9634       change->actual_trigger_element = trigger_element;
9635       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9636       change->actual_trigger_side = trigger_side;
9637       change->actual_trigger_ce_value = CustomValue[x][y];
9638       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9639
9640       /* special case: trigger element not at (x,y) position for some events */
9641       if (check_trigger_element)
9642       {
9643         static struct
9644         {
9645           int dx, dy;
9646         } move_xy[] =
9647           {
9648             {  0,  0 },
9649             { -1,  0 },
9650             { +1,  0 },
9651             {  0,  0 },
9652             {  0, -1 },
9653             {  0,  0 }, { 0, 0 }, { 0, 0 },
9654             {  0, +1 }
9655           };
9656
9657         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9658         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9659
9660         change->actual_trigger_ce_value = CustomValue[xx][yy];
9661         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9662       }
9663
9664       if (change->can_change && !change_done)
9665       {
9666         ChangeDelay[x][y] = 1;
9667         ChangeEvent[x][y] = trigger_event;
9668
9669         HandleElementChange(x, y, p);
9670
9671         change_done = TRUE;
9672       }
9673 #if USE_NEW_DELAYED_ACTION
9674       else if (change->has_action)
9675       {
9676         ExecuteCustomElementAction(x, y, element, p);
9677         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9678       }
9679 #else
9680       if (change->has_action)
9681       {
9682         ExecuteCustomElementAction(x, y, element, p);
9683         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9684       }
9685 #endif
9686     }
9687   }
9688
9689   RECURSION_LOOP_DETECTION_END();
9690
9691   return change_done;
9692 }
9693
9694 static void PlayPlayerSound(struct PlayerInfo *player)
9695 {
9696   int jx = player->jx, jy = player->jy;
9697   int sound_element = player->artwork_element;
9698   int last_action = player->last_action_waiting;
9699   int action = player->action_waiting;
9700
9701   if (player->is_waiting)
9702   {
9703     if (action != last_action)
9704       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9705     else
9706       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9707   }
9708   else
9709   {
9710     if (action != last_action)
9711       StopSound(element_info[sound_element].sound[last_action]);
9712
9713     if (last_action == ACTION_SLEEPING)
9714       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9715   }
9716 }
9717
9718 static void PlayAllPlayersSound()
9719 {
9720   int i;
9721
9722   for (i = 0; i < MAX_PLAYERS; i++)
9723     if (stored_player[i].active)
9724       PlayPlayerSound(&stored_player[i]);
9725 }
9726
9727 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9728 {
9729   boolean last_waiting = player->is_waiting;
9730   int move_dir = player->MovDir;
9731
9732   player->dir_waiting = move_dir;
9733   player->last_action_waiting = player->action_waiting;
9734
9735   if (is_waiting)
9736   {
9737     if (!last_waiting)          /* not waiting -> waiting */
9738     {
9739       player->is_waiting = TRUE;
9740
9741       player->frame_counter_bored =
9742         FrameCounter +
9743         game.player_boring_delay_fixed +
9744         GetSimpleRandom(game.player_boring_delay_random);
9745       player->frame_counter_sleeping =
9746         FrameCounter +
9747         game.player_sleeping_delay_fixed +
9748         GetSimpleRandom(game.player_sleeping_delay_random);
9749
9750       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9751     }
9752
9753     if (game.player_sleeping_delay_fixed +
9754         game.player_sleeping_delay_random > 0 &&
9755         player->anim_delay_counter == 0 &&
9756         player->post_delay_counter == 0 &&
9757         FrameCounter >= player->frame_counter_sleeping)
9758       player->is_sleeping = TRUE;
9759     else if (game.player_boring_delay_fixed +
9760              game.player_boring_delay_random > 0 &&
9761              FrameCounter >= player->frame_counter_bored)
9762       player->is_bored = TRUE;
9763
9764     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9765                               player->is_bored ? ACTION_BORING :
9766                               ACTION_WAITING);
9767
9768     if (player->is_sleeping && player->use_murphy)
9769     {
9770       /* special case for sleeping Murphy when leaning against non-free tile */
9771
9772       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9773           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9774            !IS_MOVING(player->jx - 1, player->jy)))
9775         move_dir = MV_LEFT;
9776       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9777                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9778                 !IS_MOVING(player->jx + 1, player->jy)))
9779         move_dir = MV_RIGHT;
9780       else
9781         player->is_sleeping = FALSE;
9782
9783       player->dir_waiting = move_dir;
9784     }
9785
9786     if (player->is_sleeping)
9787     {
9788       if (player->num_special_action_sleeping > 0)
9789       {
9790         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9791         {
9792           int last_special_action = player->special_action_sleeping;
9793           int num_special_action = player->num_special_action_sleeping;
9794           int special_action =
9795             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9796              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9797              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9798              last_special_action + 1 : ACTION_SLEEPING);
9799           int special_graphic =
9800             el_act_dir2img(player->artwork_element, special_action, move_dir);
9801
9802           player->anim_delay_counter =
9803             graphic_info[special_graphic].anim_delay_fixed +
9804             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9805           player->post_delay_counter =
9806             graphic_info[special_graphic].post_delay_fixed +
9807             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9808
9809           player->special_action_sleeping = special_action;
9810         }
9811
9812         if (player->anim_delay_counter > 0)
9813         {
9814           player->action_waiting = player->special_action_sleeping;
9815           player->anim_delay_counter--;
9816         }
9817         else if (player->post_delay_counter > 0)
9818         {
9819           player->post_delay_counter--;
9820         }
9821       }
9822     }
9823     else if (player->is_bored)
9824     {
9825       if (player->num_special_action_bored > 0)
9826       {
9827         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9828         {
9829           int special_action =
9830             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9831           int special_graphic =
9832             el_act_dir2img(player->artwork_element, special_action, move_dir);
9833
9834           player->anim_delay_counter =
9835             graphic_info[special_graphic].anim_delay_fixed +
9836             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9837           player->post_delay_counter =
9838             graphic_info[special_graphic].post_delay_fixed +
9839             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9840
9841           player->special_action_bored = special_action;
9842         }
9843
9844         if (player->anim_delay_counter > 0)
9845         {
9846           player->action_waiting = player->special_action_bored;
9847           player->anim_delay_counter--;
9848         }
9849         else if (player->post_delay_counter > 0)
9850         {
9851           player->post_delay_counter--;
9852         }
9853       }
9854     }
9855   }
9856   else if (last_waiting)        /* waiting -> not waiting */
9857   {
9858     player->is_waiting = FALSE;
9859     player->is_bored = FALSE;
9860     player->is_sleeping = FALSE;
9861
9862     player->frame_counter_bored = -1;
9863     player->frame_counter_sleeping = -1;
9864
9865     player->anim_delay_counter = 0;
9866     player->post_delay_counter = 0;
9867
9868     player->dir_waiting = player->MovDir;
9869     player->action_waiting = ACTION_DEFAULT;
9870
9871     player->special_action_bored = ACTION_DEFAULT;
9872     player->special_action_sleeping = ACTION_DEFAULT;
9873   }
9874 }
9875
9876 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9877 {
9878   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9879   int left      = player_action & JOY_LEFT;
9880   int right     = player_action & JOY_RIGHT;
9881   int up        = player_action & JOY_UP;
9882   int down      = player_action & JOY_DOWN;
9883   int button1   = player_action & JOY_BUTTON_1;
9884   int button2   = player_action & JOY_BUTTON_2;
9885   int dx        = (left ? -1 : right ? 1 : 0);
9886   int dy        = (up   ? -1 : down  ? 1 : 0);
9887
9888   if (!player->active || tape.pausing)
9889     return 0;
9890
9891   if (player_action)
9892   {
9893     if (button1)
9894       snapped = SnapField(player, dx, dy);
9895     else
9896     {
9897       if (button2)
9898         dropped = DropElement(player);
9899
9900       moved = MovePlayer(player, dx, dy);
9901     }
9902
9903     if (tape.single_step && tape.recording && !tape.pausing)
9904     {
9905       if (button1 || (dropped && !moved))
9906       {
9907         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9908         SnapField(player, 0, 0);                /* stop snapping */
9909       }
9910     }
9911
9912     SetPlayerWaiting(player, FALSE);
9913
9914     return player_action;
9915   }
9916   else
9917   {
9918     /* no actions for this player (no input at player's configured device) */
9919
9920     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9921     SnapField(player, 0, 0);
9922     CheckGravityMovementWhenNotMoving(player);
9923
9924     if (player->MovPos == 0)
9925       SetPlayerWaiting(player, TRUE);
9926
9927     if (player->MovPos == 0)    /* needed for tape.playing */
9928       player->is_moving = FALSE;
9929
9930     player->is_dropping = FALSE;
9931     player->is_dropping_pressed = FALSE;
9932     player->drop_pressed_delay = 0;
9933
9934     return 0;
9935   }
9936 }
9937
9938 static void CheckLevelTime()
9939 {
9940   int i;
9941
9942   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9943   {
9944     if (level.native_em_level->lev->home == 0)  /* all players at home */
9945     {
9946       PlayerWins(local_player);
9947
9948       AllPlayersGone = TRUE;
9949
9950       level.native_em_level->lev->home = -1;
9951     }
9952
9953     if (level.native_em_level->ply[0]->alive == 0 &&
9954         level.native_em_level->ply[1]->alive == 0 &&
9955         level.native_em_level->ply[2]->alive == 0 &&
9956         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9957       AllPlayersGone = TRUE;
9958   }
9959
9960   if (TimeFrames >= FRAMES_PER_SECOND)
9961   {
9962     TimeFrames = 0;
9963     TapeTime++;
9964
9965     for (i = 0; i < MAX_PLAYERS; i++)
9966     {
9967       struct PlayerInfo *player = &stored_player[i];
9968
9969       if (SHIELD_ON(player))
9970       {
9971         player->shield_normal_time_left--;
9972
9973         if (player->shield_deadly_time_left > 0)
9974           player->shield_deadly_time_left--;
9975       }
9976     }
9977
9978     if (!local_player->LevelSolved && !level.use_step_counter)
9979     {
9980       TimePlayed++;
9981
9982       if (TimeLeft > 0)
9983       {
9984         TimeLeft--;
9985
9986         if (TimeLeft <= 10 && setup.time_limit)
9987           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9988
9989         DrawGameValue_Time(TimeLeft);
9990
9991         if (!TimeLeft && setup.time_limit)
9992         {
9993           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9994             level.native_em_level->lev->killed_out_of_time = TRUE;
9995           else
9996             for (i = 0; i < MAX_PLAYERS; i++)
9997               KillPlayer(&stored_player[i]);
9998         }
9999       }
10000       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10001         DrawGameValue_Time(TimePlayed);
10002
10003       level.native_em_level->lev->time =
10004         (level.time == 0 ? TimePlayed : TimeLeft);
10005     }
10006
10007     if (tape.recording || tape.playing)
10008       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10009   }
10010 }
10011
10012 void AdvanceFrameAndPlayerCounters(int player_nr)
10013 {
10014   int i;
10015
10016   /* advance frame counters (global frame counter and time frame counter) */
10017   FrameCounter++;
10018   TimeFrames++;
10019
10020   /* advance player counters (counters for move delay, move animation etc.) */
10021   for (i = 0; i < MAX_PLAYERS; i++)
10022   {
10023     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10024     int move_delay_value = stored_player[i].move_delay_value;
10025     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10026
10027     if (!advance_player_counters)       /* not all players may be affected */
10028       continue;
10029
10030 #if USE_NEW_PLAYER_ANIM
10031     if (move_frames == 0)       /* less than one move per game frame */
10032     {
10033       int stepsize = TILEX / move_delay_value;
10034       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10035       int count = (stored_player[i].is_moving ?
10036                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10037
10038       if (count % delay == 0)
10039         move_frames = 1;
10040     }
10041 #endif
10042
10043     stored_player[i].Frame += move_frames;
10044
10045     if (stored_player[i].MovPos != 0)
10046       stored_player[i].StepFrame += move_frames;
10047
10048     if (stored_player[i].move_delay > 0)
10049       stored_player[i].move_delay--;
10050
10051     /* due to bugs in previous versions, counter must count up, not down */
10052     if (stored_player[i].push_delay != -1)
10053       stored_player[i].push_delay++;
10054
10055     if (stored_player[i].drop_delay > 0)
10056       stored_player[i].drop_delay--;
10057
10058     if (stored_player[i].is_dropping_pressed)
10059       stored_player[i].drop_pressed_delay++;
10060   }
10061 }
10062
10063 void StartGameActions(boolean init_network_game, boolean record_tape,
10064                       long random_seed)
10065 {
10066   unsigned long new_random_seed = InitRND(random_seed);
10067
10068   if (record_tape)
10069     TapeStartRecording(new_random_seed);
10070
10071 #if defined(NETWORK_AVALIABLE)
10072   if (init_network_game)
10073   {
10074     SendToServer_StartPlaying();
10075
10076     return;
10077   }
10078 #endif
10079
10080   InitGame();
10081 }
10082
10083 void GameActions()
10084 {
10085   static unsigned long game_frame_delay = 0;
10086   unsigned long game_frame_delay_value;
10087   byte *recorded_player_action;
10088   byte summarized_player_action = 0;
10089   byte tape_action[MAX_PLAYERS];
10090   int i;
10091
10092   /* detect endless loops, caused by custom element programming */
10093   if (recursion_loop_detected && recursion_loop_depth == 0)
10094   {
10095     char *message = getStringCat3("Internal Error ! Element ",
10096                                   EL_NAME(recursion_loop_element),
10097                                   " caused endless loop ! Quit the game ?");
10098
10099     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10100           EL_NAME(recursion_loop_element));
10101
10102     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10103
10104     recursion_loop_detected = FALSE;    /* if game should be continued */
10105
10106     free(message);
10107
10108     return;
10109   }
10110
10111   if (game.restart_level)
10112     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
10113
10114   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10115   {
10116     if (level.native_em_level->lev->home == 0)  /* all players at home */
10117     {
10118       PlayerWins(local_player);
10119
10120       AllPlayersGone = TRUE;
10121
10122       level.native_em_level->lev->home = -1;
10123     }
10124
10125     if (level.native_em_level->ply[0]->alive == 0 &&
10126         level.native_em_level->ply[1]->alive == 0 &&
10127         level.native_em_level->ply[2]->alive == 0 &&
10128         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10129       AllPlayersGone = TRUE;
10130   }
10131
10132   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
10133     GameWon();
10134
10135   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
10136     TapeStop();
10137
10138   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
10139     return;
10140
10141   game_frame_delay_value =
10142     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
10143
10144   if (tape.playing && tape.warp_forward && !tape.pausing)
10145     game_frame_delay_value = 0;
10146
10147   /* ---------- main game synchronization point ---------- */
10148
10149   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
10150
10151   if (network_playing && !network_player_action_received)
10152   {
10153     /* try to get network player actions in time */
10154
10155 #if defined(NETWORK_AVALIABLE)
10156     /* last chance to get network player actions without main loop delay */
10157     HandleNetworking();
10158 #endif
10159
10160     /* game was quit by network peer */
10161     if (game_status != GAME_MODE_PLAYING)
10162       return;
10163
10164     if (!network_player_action_received)
10165       return;           /* failed to get network player actions in time */
10166
10167     /* do not yet reset "network_player_action_received" (for tape.pausing) */
10168   }
10169
10170   if (tape.pausing)
10171     return;
10172
10173   /* at this point we know that we really continue executing the game */
10174
10175   network_player_action_received = FALSE;
10176
10177   /* when playing tape, read previously recorded player input from tape data */
10178   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
10179
10180 #if 1
10181   /* TapePlayAction() may return NULL when toggling to "pause before death" */
10182   if (tape.pausing)
10183     return;
10184 #endif
10185
10186   if (tape.set_centered_player)
10187   {
10188     game.centered_player_nr_next = tape.centered_player_nr_next;
10189     game.set_centered_player = TRUE;
10190   }
10191
10192   for (i = 0; i < MAX_PLAYERS; i++)
10193   {
10194     summarized_player_action |= stored_player[i].action;
10195
10196     if (!network_playing)
10197       stored_player[i].effective_action = stored_player[i].action;
10198   }
10199
10200 #if defined(NETWORK_AVALIABLE)
10201   if (network_playing)
10202     SendToServer_MovePlayer(summarized_player_action);
10203 #endif
10204
10205   if (!options.network && !setup.team_mode)
10206     local_player->effective_action = summarized_player_action;
10207
10208   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
10209   {
10210     for (i = 0; i < MAX_PLAYERS; i++)
10211       stored_player[i].effective_action =
10212         (i == game.centered_player_nr ? summarized_player_action : 0);
10213   }
10214
10215   if (recorded_player_action != NULL)
10216     for (i = 0; i < MAX_PLAYERS; i++)
10217       stored_player[i].effective_action = recorded_player_action[i];
10218
10219   for (i = 0; i < MAX_PLAYERS; i++)
10220   {
10221     tape_action[i] = stored_player[i].effective_action;
10222
10223     /* (this can only happen in the R'n'D game engine) */
10224     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10225       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10226   }
10227
10228   /* only record actions from input devices, but not programmed actions */
10229   if (tape.recording)
10230     TapeRecordAction(tape_action);
10231
10232   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10233   {
10234     GameActions_EM_Main();
10235   }
10236   else
10237   {
10238     GameActions_RND();
10239   }
10240 }
10241
10242 void GameActions_EM_Main()
10243 {
10244   byte effective_action[MAX_PLAYERS];
10245   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10246   int i;
10247
10248   for (i = 0; i < MAX_PLAYERS; i++)
10249     effective_action[i] = stored_player[i].effective_action;
10250
10251   GameActions_EM(effective_action, warp_mode);
10252
10253   CheckLevelTime();
10254
10255   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10256 }
10257
10258 void GameActions_RND()
10259 {
10260   int magic_wall_x = 0, magic_wall_y = 0;
10261   int i, x, y, element, graphic;
10262
10263   InitPlayfieldScanModeVars();
10264
10265 #if USE_ONE_MORE_CHANGE_PER_FRAME
10266   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10267   {
10268     SCAN_PLAYFIELD(x, y)
10269     {
10270       ChangeCount[x][y] = 0;
10271       ChangeEvent[x][y] = -1;
10272     }
10273   }
10274 #endif
10275
10276   if (game.set_centered_player)
10277   {
10278     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10279
10280     /* switching to "all players" only possible if all players fit to screen */
10281     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10282     {
10283       game.centered_player_nr_next = game.centered_player_nr;
10284       game.set_centered_player = FALSE;
10285     }
10286
10287     /* do not switch focus to non-existing (or non-active) player */
10288     if (game.centered_player_nr_next >= 0 &&
10289         !stored_player[game.centered_player_nr_next].active)
10290     {
10291       game.centered_player_nr_next = game.centered_player_nr;
10292       game.set_centered_player = FALSE;
10293     }
10294   }
10295
10296   if (game.set_centered_player &&
10297       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10298   {
10299     int sx, sy;
10300
10301     if (game.centered_player_nr_next == -1)
10302     {
10303       setScreenCenteredToAllPlayers(&sx, &sy);
10304     }
10305     else
10306     {
10307       sx = stored_player[game.centered_player_nr_next].jx;
10308       sy = stored_player[game.centered_player_nr_next].jy;
10309     }
10310
10311     game.centered_player_nr = game.centered_player_nr_next;
10312     game.set_centered_player = FALSE;
10313
10314     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10315     DrawGameDoorValues();
10316   }
10317
10318   for (i = 0; i < MAX_PLAYERS; i++)
10319   {
10320     int actual_player_action = stored_player[i].effective_action;
10321
10322 #if 1
10323     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10324        - rnd_equinox_tetrachloride 048
10325        - rnd_equinox_tetrachloride_ii 096
10326        - rnd_emanuel_schmieg 002
10327        - doctor_sloan_ww 001, 020
10328     */
10329     if (stored_player[i].MovPos == 0)
10330       CheckGravityMovement(&stored_player[i]);
10331 #endif
10332
10333     /* overwrite programmed action with tape action */
10334     if (stored_player[i].programmed_action)
10335       actual_player_action = stored_player[i].programmed_action;
10336
10337     PlayerActions(&stored_player[i], actual_player_action);
10338
10339     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10340   }
10341
10342   ScrollScreen(NULL, SCROLL_GO_ON);
10343
10344   /* for backwards compatibility, the following code emulates a fixed bug that
10345      occured when pushing elements (causing elements that just made their last
10346      pushing step to already (if possible) make their first falling step in the
10347      same game frame, which is bad); this code is also needed to use the famous
10348      "spring push bug" which is used in older levels and might be wanted to be
10349      used also in newer levels, but in this case the buggy pushing code is only
10350      affecting the "spring" element and no other elements */
10351
10352   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10353   {
10354     for (i = 0; i < MAX_PLAYERS; i++)
10355     {
10356       struct PlayerInfo *player = &stored_player[i];
10357       int x = player->jx;
10358       int y = player->jy;
10359
10360       if (player->active && player->is_pushing && player->is_moving &&
10361           IS_MOVING(x, y) &&
10362           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10363            Feld[x][y] == EL_SPRING))
10364       {
10365         ContinueMoving(x, y);
10366
10367         /* continue moving after pushing (this is actually a bug) */
10368         if (!IS_MOVING(x, y))
10369           Stop[x][y] = FALSE;
10370       }
10371     }
10372   }
10373
10374 #if 0
10375   debug_print_timestamp(0, "start main loop profiling");
10376 #endif
10377
10378   SCAN_PLAYFIELD(x, y)
10379   {
10380     ChangeCount[x][y] = 0;
10381     ChangeEvent[x][y] = -1;
10382
10383     /* this must be handled before main playfield loop */
10384     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10385     {
10386       MovDelay[x][y]--;
10387       if (MovDelay[x][y] <= 0)
10388         RemoveField(x, y);
10389     }
10390
10391 #if USE_NEW_SNAP_DELAY
10392     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10393     {
10394       MovDelay[x][y]--;
10395       if (MovDelay[x][y] <= 0)
10396       {
10397         RemoveField(x, y);
10398         DrawLevelField(x, y);
10399
10400         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10401       }
10402     }
10403 #endif
10404
10405 #if DEBUG
10406     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10407     {
10408       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10409       printf("GameActions(): This should never happen!\n");
10410
10411       ChangePage[x][y] = -1;
10412     }
10413 #endif
10414
10415     Stop[x][y] = FALSE;
10416     if (WasJustMoving[x][y] > 0)
10417       WasJustMoving[x][y]--;
10418     if (WasJustFalling[x][y] > 0)
10419       WasJustFalling[x][y]--;
10420     if (CheckCollision[x][y] > 0)
10421       CheckCollision[x][y]--;
10422     if (CheckImpact[x][y] > 0)
10423       CheckImpact[x][y]--;
10424
10425     GfxFrame[x][y]++;
10426
10427     /* reset finished pushing action (not done in ContinueMoving() to allow
10428        continuous pushing animation for elements with zero push delay) */
10429     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10430     {
10431       ResetGfxAnimation(x, y);
10432       DrawLevelField(x, y);
10433     }
10434
10435 #if DEBUG
10436     if (IS_BLOCKED(x, y))
10437     {
10438       int oldx, oldy;
10439
10440       Blocked2Moving(x, y, &oldx, &oldy);
10441       if (!IS_MOVING(oldx, oldy))
10442       {
10443         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10444         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10445         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10446         printf("GameActions(): This should never happen!\n");
10447       }
10448     }
10449 #endif
10450   }
10451
10452 #if 0
10453   debug_print_timestamp(0, "- time for pre-main loop:");
10454 #endif
10455
10456 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10457   SCAN_PLAYFIELD(x, y)
10458   {
10459     element = Feld[x][y];
10460     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10461
10462 #if 1
10463     {
10464 #if 1
10465       int element2 = element;
10466       int graphic2 = graphic;
10467 #else
10468       int element2 = Feld[x][y];
10469       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10470 #endif
10471       int last_gfx_frame = GfxFrame[x][y];
10472
10473       if (graphic_info[graphic2].anim_global_sync)
10474         GfxFrame[x][y] = FrameCounter;
10475       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10476         GfxFrame[x][y] = CustomValue[x][y];
10477       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10478         GfxFrame[x][y] = element_info[element2].collect_score;
10479       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10480         GfxFrame[x][y] = ChangeDelay[x][y];
10481
10482       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10483         DrawLevelGraphicAnimation(x, y, graphic2);
10484     }
10485 #else
10486     ResetGfxFrame(x, y, TRUE);
10487 #endif
10488
10489 #if 1
10490     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10491         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10492       ResetRandomAnimationValue(x, y);
10493 #endif
10494
10495 #if 1
10496     SetRandomAnimationValue(x, y);
10497 #endif
10498
10499 #if 1
10500     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10501 #endif
10502   }
10503 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10504
10505 #if 0
10506   debug_print_timestamp(0, "- time for TEST loop:     -->");
10507 #endif
10508
10509   SCAN_PLAYFIELD(x, y)
10510   {
10511     element = Feld[x][y];
10512     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10513
10514     ResetGfxFrame(x, y, TRUE);
10515
10516     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10517         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10518       ResetRandomAnimationValue(x, y);
10519
10520     SetRandomAnimationValue(x, y);
10521
10522     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10523
10524     if (IS_INACTIVE(element))
10525     {
10526       if (IS_ANIMATED(graphic))
10527         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10528
10529       continue;
10530     }
10531
10532     /* this may take place after moving, so 'element' may have changed */
10533     if (IS_CHANGING(x, y) &&
10534         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10535     {
10536       int page = element_info[element].event_page_nr[CE_DELAY];
10537
10538 #if 1
10539       HandleElementChange(x, y, page);
10540 #else
10541       if (CAN_CHANGE(element))
10542         HandleElementChange(x, y, page);
10543
10544       if (HAS_ACTION(element))
10545         ExecuteCustomElementAction(x, y, element, page);
10546 #endif
10547
10548       element = Feld[x][y];
10549       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10550     }
10551
10552 #if 0   // ---------------------------------------------------------------------
10553
10554     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10555     {
10556       StartMoving(x, y);
10557
10558       element = Feld[x][y];
10559       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10560
10561       if (IS_ANIMATED(graphic) &&
10562           !IS_MOVING(x, y) &&
10563           !Stop[x][y])
10564         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10565
10566       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10567         DrawTwinkleOnField(x, y);
10568     }
10569     else if (IS_MOVING(x, y))
10570       ContinueMoving(x, y);
10571     else
10572     {
10573       switch (element)
10574       {
10575         case EL_ACID:
10576         case EL_EXIT_OPEN:
10577         case EL_EM_EXIT_OPEN:
10578         case EL_SP_EXIT_OPEN:
10579         case EL_STEEL_EXIT_OPEN:
10580         case EL_EM_STEEL_EXIT_OPEN:
10581         case EL_SP_TERMINAL:
10582         case EL_SP_TERMINAL_ACTIVE:
10583         case EL_EXTRA_TIME:
10584         case EL_SHIELD_NORMAL:
10585         case EL_SHIELD_DEADLY:
10586           if (IS_ANIMATED(graphic))
10587             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10588           break;
10589
10590         case EL_DYNAMITE_ACTIVE:
10591         case EL_EM_DYNAMITE_ACTIVE:
10592         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10593         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10594         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10595         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10596         case EL_SP_DISK_RED_ACTIVE:
10597           CheckDynamite(x, y);
10598           break;
10599
10600         case EL_AMOEBA_GROWING:
10601           AmoebeWaechst(x, y);
10602           break;
10603
10604         case EL_AMOEBA_SHRINKING:
10605           AmoebaDisappearing(x, y);
10606           break;
10607
10608 #if !USE_NEW_AMOEBA_CODE
10609         case EL_AMOEBA_WET:
10610         case EL_AMOEBA_DRY:
10611         case EL_AMOEBA_FULL:
10612         case EL_BD_AMOEBA:
10613         case EL_EMC_DRIPPER:
10614           AmoebeAbleger(x, y);
10615           break;
10616 #endif
10617
10618         case EL_GAME_OF_LIFE:
10619         case EL_BIOMAZE:
10620           Life(x, y);
10621           break;
10622
10623         case EL_EXIT_CLOSED:
10624           CheckExit(x, y);
10625           break;
10626
10627         case EL_EM_EXIT_CLOSED:
10628           CheckExitEM(x, y);
10629           break;
10630
10631         case EL_STEEL_EXIT_CLOSED:
10632           CheckExitSteel(x, y);
10633           break;
10634
10635         case EL_EM_STEEL_EXIT_CLOSED:
10636           CheckExitSteelEM(x, y);
10637           break;
10638
10639         case EL_SP_EXIT_CLOSED:
10640           CheckExitSP(x, y);
10641           break;
10642
10643         case EL_EXPANDABLE_WALL_GROWING:
10644         case EL_EXPANDABLE_STEELWALL_GROWING:
10645           MauerWaechst(x, y);
10646           break;
10647
10648         case EL_EXPANDABLE_WALL:
10649         case EL_EXPANDABLE_WALL_HORIZONTAL:
10650         case EL_EXPANDABLE_WALL_VERTICAL:
10651         case EL_EXPANDABLE_WALL_ANY:
10652         case EL_BD_EXPANDABLE_WALL:
10653           MauerAbleger(x, y);
10654           break;
10655
10656         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10657         case EL_EXPANDABLE_STEELWALL_VERTICAL:
10658         case EL_EXPANDABLE_STEELWALL_ANY:
10659           MauerAblegerStahl(x, y);
10660           break;
10661
10662         case EL_FLAMES:
10663           CheckForDragon(x, y);
10664           break;
10665
10666         case EL_EXPLOSION:
10667           break;
10668
10669         case EL_ELEMENT_SNAPPING:
10670         case EL_DIAGONAL_SHRINKING:
10671         case EL_DIAGONAL_GROWING:
10672         {
10673           graphic =
10674             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10675
10676           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10677           break;
10678         }
10679
10680         default:
10681           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10682             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10683           break;
10684       }
10685     }
10686
10687 #else   // ---------------------------------------------------------------------
10688
10689     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10690     {
10691       StartMoving(x, y);
10692
10693       element = Feld[x][y];
10694       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10695
10696       if (IS_ANIMATED(graphic) &&
10697           !IS_MOVING(x, y) &&
10698           !Stop[x][y])
10699         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10700
10701       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10702         DrawTwinkleOnField(x, y);
10703     }
10704     else if ((element == EL_ACID ||
10705               element == EL_EXIT_OPEN ||
10706               element == EL_EM_EXIT_OPEN ||
10707               element == EL_SP_EXIT_OPEN ||
10708               element == EL_STEEL_EXIT_OPEN ||
10709               element == EL_EM_STEEL_EXIT_OPEN ||
10710               element == EL_SP_TERMINAL ||
10711               element == EL_SP_TERMINAL_ACTIVE ||
10712               element == EL_EXTRA_TIME ||
10713               element == EL_SHIELD_NORMAL ||
10714               element == EL_SHIELD_DEADLY) &&
10715              IS_ANIMATED(graphic))
10716       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10717     else if (IS_MOVING(x, y))
10718       ContinueMoving(x, y);
10719     else if (IS_ACTIVE_BOMB(element))
10720       CheckDynamite(x, y);
10721     else if (element == EL_AMOEBA_GROWING)
10722       AmoebeWaechst(x, y);
10723     else if (element == EL_AMOEBA_SHRINKING)
10724       AmoebaDisappearing(x, y);
10725
10726 #if !USE_NEW_AMOEBA_CODE
10727     else if (IS_AMOEBALIVE(element))
10728       AmoebeAbleger(x, y);
10729 #endif
10730
10731     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10732       Life(x, y);
10733     else if (element == EL_EXIT_CLOSED)
10734       CheckExit(x, y);
10735     else if (element == EL_EM_EXIT_CLOSED)
10736       CheckExitEM(x, y);
10737     else if (element == EL_STEEL_EXIT_CLOSED)
10738       CheckExitSteel(x, y);
10739     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10740       CheckExitSteelEM(x, y);
10741     else if (element == EL_SP_EXIT_CLOSED)
10742       CheckExitSP(x, y);
10743     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10744              element == EL_EXPANDABLE_STEELWALL_GROWING)
10745       MauerWaechst(x, y);
10746     else if (element == EL_EXPANDABLE_WALL ||
10747              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10748              element == EL_EXPANDABLE_WALL_VERTICAL ||
10749              element == EL_EXPANDABLE_WALL_ANY ||
10750              element == EL_BD_EXPANDABLE_WALL)
10751       MauerAbleger(x, y);
10752     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10753              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10754              element == EL_EXPANDABLE_STEELWALL_ANY)
10755       MauerAblegerStahl(x, y);
10756     else if (element == EL_FLAMES)
10757       CheckForDragon(x, y);
10758     else if (element == EL_EXPLOSION)
10759       ; /* drawing of correct explosion animation is handled separately */
10760     else if (element == EL_ELEMENT_SNAPPING ||
10761              element == EL_DIAGONAL_SHRINKING ||
10762              element == EL_DIAGONAL_GROWING)
10763     {
10764       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10765
10766       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10767     }
10768     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10769       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10770
10771 #endif  // ---------------------------------------------------------------------
10772
10773     if (IS_BELT_ACTIVE(element))
10774       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10775
10776     if (game.magic_wall_active)
10777     {
10778       int jx = local_player->jx, jy = local_player->jy;
10779
10780       /* play the element sound at the position nearest to the player */
10781       if ((element == EL_MAGIC_WALL_FULL ||
10782            element == EL_MAGIC_WALL_ACTIVE ||
10783            element == EL_MAGIC_WALL_EMPTYING ||
10784            element == EL_BD_MAGIC_WALL_FULL ||
10785            element == EL_BD_MAGIC_WALL_ACTIVE ||
10786            element == EL_BD_MAGIC_WALL_EMPTYING ||
10787            element == EL_DC_MAGIC_WALL_FULL ||
10788            element == EL_DC_MAGIC_WALL_ACTIVE ||
10789            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10790           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10791       {
10792         magic_wall_x = x;
10793         magic_wall_y = y;
10794       }
10795     }
10796   }
10797
10798 #if 0
10799   debug_print_timestamp(0, "- time for MAIN loop:     -->");
10800 #endif
10801
10802 #if USE_NEW_AMOEBA_CODE
10803   /* new experimental amoeba growth stuff */
10804   if (!(FrameCounter % 8))
10805   {
10806     static unsigned long random = 1684108901;
10807
10808     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10809     {
10810       x = RND(lev_fieldx);
10811       y = RND(lev_fieldy);
10812       element = Feld[x][y];
10813
10814       if (!IS_PLAYER(x,y) &&
10815           (element == EL_EMPTY ||
10816            CAN_GROW_INTO(element) ||
10817            element == EL_QUICKSAND_EMPTY ||
10818            element == EL_QUICKSAND_FAST_EMPTY ||
10819            element == EL_ACID_SPLASH_LEFT ||
10820            element == EL_ACID_SPLASH_RIGHT))
10821       {
10822         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10823             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10824             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10825             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10826           Feld[x][y] = EL_AMOEBA_DROP;
10827       }
10828
10829       random = random * 129 + 1;
10830     }
10831   }
10832 #endif
10833
10834 #if 0
10835   if (game.explosions_delayed)
10836 #endif
10837   {
10838     game.explosions_delayed = FALSE;
10839
10840     SCAN_PLAYFIELD(x, y)
10841     {
10842       element = Feld[x][y];
10843
10844       if (ExplodeField[x][y])
10845         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10846       else if (element == EL_EXPLOSION)
10847         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10848
10849       ExplodeField[x][y] = EX_TYPE_NONE;
10850     }
10851
10852     game.explosions_delayed = TRUE;
10853   }
10854
10855   if (game.magic_wall_active)
10856   {
10857     if (!(game.magic_wall_time_left % 4))
10858     {
10859       int element = Feld[magic_wall_x][magic_wall_y];
10860
10861       if (element == EL_BD_MAGIC_WALL_FULL ||
10862           element == EL_BD_MAGIC_WALL_ACTIVE ||
10863           element == EL_BD_MAGIC_WALL_EMPTYING)
10864         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10865       else if (element == EL_DC_MAGIC_WALL_FULL ||
10866                element == EL_DC_MAGIC_WALL_ACTIVE ||
10867                element == EL_DC_MAGIC_WALL_EMPTYING)
10868         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10869       else
10870         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10871     }
10872
10873     if (game.magic_wall_time_left > 0)
10874     {
10875       game.magic_wall_time_left--;
10876       if (!game.magic_wall_time_left)
10877       {
10878         SCAN_PLAYFIELD(x, y)
10879         {
10880           element = Feld[x][y];
10881
10882           if (element == EL_MAGIC_WALL_ACTIVE ||
10883               element == EL_MAGIC_WALL_FULL)
10884           {
10885             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10886             DrawLevelField(x, y);
10887           }
10888           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10889                    element == EL_BD_MAGIC_WALL_FULL)
10890           {
10891             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10892             DrawLevelField(x, y);
10893           }
10894           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10895                    element == EL_DC_MAGIC_WALL_FULL)
10896           {
10897             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10898             DrawLevelField(x, y);
10899           }
10900         }
10901
10902         game.magic_wall_active = FALSE;
10903       }
10904     }
10905   }
10906
10907   if (game.light_time_left > 0)
10908   {
10909     game.light_time_left--;
10910
10911     if (game.light_time_left == 0)
10912       RedrawAllLightSwitchesAndInvisibleElements();
10913   }
10914
10915   if (game.timegate_time_left > 0)
10916   {
10917     game.timegate_time_left--;
10918
10919     if (game.timegate_time_left == 0)
10920       CloseAllOpenTimegates();
10921   }
10922
10923   if (game.lenses_time_left > 0)
10924   {
10925     game.lenses_time_left--;
10926
10927     if (game.lenses_time_left == 0)
10928       RedrawAllInvisibleElementsForLenses();
10929   }
10930
10931   if (game.magnify_time_left > 0)
10932   {
10933     game.magnify_time_left--;
10934
10935     if (game.magnify_time_left == 0)
10936       RedrawAllInvisibleElementsForMagnifier();
10937   }
10938
10939   for (i = 0; i < MAX_PLAYERS; i++)
10940   {
10941     struct PlayerInfo *player = &stored_player[i];
10942
10943     if (SHIELD_ON(player))
10944     {
10945       if (player->shield_deadly_time_left)
10946         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10947       else if (player->shield_normal_time_left)
10948         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10949     }
10950   }
10951
10952   CheckLevelTime();
10953
10954   DrawAllPlayers();
10955   PlayAllPlayersSound();
10956
10957   if (options.debug)                    /* calculate frames per second */
10958   {
10959     static unsigned long fps_counter = 0;
10960     static int fps_frames = 0;
10961     unsigned long fps_delay_ms = Counter() - fps_counter;
10962
10963     fps_frames++;
10964
10965     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10966     {
10967       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10968
10969       fps_frames = 0;
10970       fps_counter = Counter();
10971     }
10972
10973     redraw_mask |= REDRAW_FPS;
10974   }
10975
10976   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10977
10978   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10979   {
10980     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10981
10982     local_player->show_envelope = 0;
10983   }
10984
10985 #if 0
10986   debug_print_timestamp(0, "stop main loop profiling ");
10987   printf("----------------------------------------------------------\n");
10988 #endif
10989
10990   /* use random number generator in every frame to make it less predictable */
10991   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10992     RND(1);
10993 }
10994
10995 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10996 {
10997   int min_x = x, min_y = y, max_x = x, max_y = y;
10998   int i;
10999
11000   for (i = 0; i < MAX_PLAYERS; i++)
11001   {
11002     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11003
11004     if (!stored_player[i].active || &stored_player[i] == player)
11005       continue;
11006
11007     min_x = MIN(min_x, jx);
11008     min_y = MIN(min_y, jy);
11009     max_x = MAX(max_x, jx);
11010     max_y = MAX(max_y, jy);
11011   }
11012
11013   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11014 }
11015
11016 static boolean AllPlayersInVisibleScreen()
11017 {
11018   int i;
11019
11020   for (i = 0; i < MAX_PLAYERS; i++)
11021   {
11022     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11023
11024     if (!stored_player[i].active)
11025       continue;
11026
11027     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11028       return FALSE;
11029   }
11030
11031   return TRUE;
11032 }
11033
11034 void ScrollLevel(int dx, int dy)
11035 {
11036 #if 1
11037   static Bitmap *bitmap_db_field2 = NULL;
11038   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11039   int x, y;
11040 #else
11041   int i, x, y;
11042 #endif
11043
11044 #if 0
11045   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11046   /* only horizontal XOR vertical scroll direction allowed */
11047   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11048     return;
11049 #endif
11050
11051 #if 1
11052   if (bitmap_db_field2 == NULL)
11053     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11054
11055   /* needed when blitting directly to same bitmap -- should not be needed with
11056      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11057   BlitBitmap(drawto_field, bitmap_db_field2,
11058              FX + TILEX * (dx == -1) - softscroll_offset,
11059              FY + TILEY * (dy == -1) - softscroll_offset,
11060              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11061              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11062              FX + TILEX * (dx == 1) - softscroll_offset,
11063              FY + TILEY * (dy == 1) - softscroll_offset);
11064   BlitBitmap(bitmap_db_field2, drawto_field,
11065              FX + TILEX * (dx == 1) - softscroll_offset,
11066              FY + TILEY * (dy == 1) - softscroll_offset,
11067              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11068              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11069              FX + TILEX * (dx == 1) - softscroll_offset,
11070              FY + TILEY * (dy == 1) - softscroll_offset);
11071
11072 #else
11073
11074 #if 1
11075   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11076   int xsize = (BX2 - BX1 + 1);
11077   int ysize = (BY2 - BY1 + 1);
11078   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11079   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11080   int step  = (start < end ? +1 : -1);
11081
11082   for (i = start; i != end; i += step)
11083   {
11084     BlitBitmap(drawto_field, drawto_field,
11085                FX + TILEX * (dx != 0 ? i + step : 0),
11086                FY + TILEY * (dy != 0 ? i + step : 0),
11087                TILEX * (dx != 0 ? 1 : xsize),
11088                TILEY * (dy != 0 ? 1 : ysize),
11089                FX + TILEX * (dx != 0 ? i : 0),
11090                FY + TILEY * (dy != 0 ? i : 0));
11091   }
11092
11093 #else
11094
11095   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11096
11097   BlitBitmap(drawto_field, drawto_field,
11098              FX + TILEX * (dx == -1) - softscroll_offset,
11099              FY + TILEY * (dy == -1) - softscroll_offset,
11100              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11101              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11102              FX + TILEX * (dx == 1) - softscroll_offset,
11103              FY + TILEY * (dy == 1) - softscroll_offset);
11104 #endif
11105 #endif
11106
11107   if (dx != 0)
11108   {
11109     x = (dx == 1 ? BX1 : BX2);
11110     for (y = BY1; y <= BY2; y++)
11111       DrawScreenField(x, y);
11112   }
11113
11114   if (dy != 0)
11115   {
11116     y = (dy == 1 ? BY1 : BY2);
11117     for (x = BX1; x <= BX2; x++)
11118       DrawScreenField(x, y);
11119   }
11120
11121   redraw_mask |= REDRAW_FIELD;
11122 }
11123
11124 static boolean canFallDown(struct PlayerInfo *player)
11125 {
11126   int jx = player->jx, jy = player->jy;
11127
11128   return (IN_LEV_FIELD(jx, jy + 1) &&
11129           (IS_FREE(jx, jy + 1) ||
11130            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11131           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11132           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11133 }
11134
11135 static boolean canPassField(int x, int y, int move_dir)
11136 {
11137   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11138   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11139   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11140   int nextx = x + dx;
11141   int nexty = y + dy;
11142   int element = Feld[x][y];
11143
11144   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11145           !CAN_MOVE(element) &&
11146           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11147           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11148           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11149 }
11150
11151 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11152 {
11153   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11154   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11155   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11156   int newx = x + dx;
11157   int newy = y + dy;
11158
11159   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11160           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11161           (IS_DIGGABLE(Feld[newx][newy]) ||
11162            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11163            canPassField(newx, newy, move_dir)));
11164 }
11165
11166 static void CheckGravityMovement(struct PlayerInfo *player)
11167 {
11168 #if USE_PLAYER_GRAVITY
11169   if (player->gravity && !player->programmed_action)
11170 #else
11171   if (game.gravity && !player->programmed_action)
11172 #endif
11173   {
11174     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11175     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11176     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11177     int jx = player->jx, jy = player->jy;
11178     boolean player_is_moving_to_valid_field =
11179       (!player_is_snapping &&
11180        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11181         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11182     boolean player_can_fall_down = canFallDown(player);
11183
11184     if (player_can_fall_down &&
11185         !player_is_moving_to_valid_field)
11186       player->programmed_action = MV_DOWN;
11187   }
11188 }
11189
11190 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11191 {
11192   return CheckGravityMovement(player);
11193
11194 #if USE_PLAYER_GRAVITY
11195   if (player->gravity && !player->programmed_action)
11196 #else
11197   if (game.gravity && !player->programmed_action)
11198 #endif
11199   {
11200     int jx = player->jx, jy = player->jy;
11201     boolean field_under_player_is_free =
11202       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11203     boolean player_is_standing_on_valid_field =
11204       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11205        (IS_WALKABLE(Feld[jx][jy]) &&
11206         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11207
11208     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11209       player->programmed_action = MV_DOWN;
11210   }
11211 }
11212
11213 /*
11214   MovePlayerOneStep()
11215   -----------------------------------------------------------------------------
11216   dx, dy:               direction (non-diagonal) to try to move the player to
11217   real_dx, real_dy:     direction as read from input device (can be diagonal)
11218 */
11219
11220 boolean MovePlayerOneStep(struct PlayerInfo *player,
11221                           int dx, int dy, int real_dx, int real_dy)
11222 {
11223   int jx = player->jx, jy = player->jy;
11224   int new_jx = jx + dx, new_jy = jy + dy;
11225 #if !USE_FIXED_DONT_RUN_INTO
11226   int element;
11227 #endif
11228   int can_move;
11229   boolean player_can_move = !player->cannot_move;
11230
11231   if (!player->active || (!dx && !dy))
11232     return MP_NO_ACTION;
11233
11234   player->MovDir = (dx < 0 ? MV_LEFT :
11235                     dx > 0 ? MV_RIGHT :
11236                     dy < 0 ? MV_UP :
11237                     dy > 0 ? MV_DOWN :  MV_NONE);
11238
11239   if (!IN_LEV_FIELD(new_jx, new_jy))
11240     return MP_NO_ACTION;
11241
11242   if (!player_can_move)
11243   {
11244     if (player->MovPos == 0)
11245     {
11246       player->is_moving = FALSE;
11247       player->is_digging = FALSE;
11248       player->is_collecting = FALSE;
11249       player->is_snapping = FALSE;
11250       player->is_pushing = FALSE;
11251     }
11252   }
11253
11254 #if 1
11255   if (!options.network && game.centered_player_nr == -1 &&
11256       !AllPlayersInSight(player, new_jx, new_jy))
11257     return MP_NO_ACTION;
11258 #else
11259   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11260     return MP_NO_ACTION;
11261 #endif
11262
11263 #if !USE_FIXED_DONT_RUN_INTO
11264   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11265
11266   /* (moved to DigField()) */
11267   if (player_can_move && DONT_RUN_INTO(element))
11268   {
11269     if (element == EL_ACID && dx == 0 && dy == 1)
11270     {
11271       SplashAcid(new_jx, new_jy);
11272       Feld[jx][jy] = EL_PLAYER_1;
11273       InitMovingField(jx, jy, MV_DOWN);
11274       Store[jx][jy] = EL_ACID;
11275       ContinueMoving(jx, jy);
11276       BuryPlayer(player);
11277     }
11278     else
11279       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11280
11281     return MP_MOVING;
11282   }
11283 #endif
11284
11285   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11286   if (can_move != MP_MOVING)
11287     return can_move;
11288
11289   /* check if DigField() has caused relocation of the player */
11290   if (player->jx != jx || player->jy != jy)
11291     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11292
11293   StorePlayer[jx][jy] = 0;
11294   player->last_jx = jx;
11295   player->last_jy = jy;
11296   player->jx = new_jx;
11297   player->jy = new_jy;
11298   StorePlayer[new_jx][new_jy] = player->element_nr;
11299
11300   if (player->move_delay_value_next != -1)
11301   {
11302     player->move_delay_value = player->move_delay_value_next;
11303     player->move_delay_value_next = -1;
11304   }
11305
11306   player->MovPos =
11307     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11308
11309   player->step_counter++;
11310
11311   PlayerVisit[jx][jy] = FrameCounter;
11312
11313 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11314   player->is_moving = TRUE;
11315 #endif
11316
11317 #if 1
11318   /* should better be called in MovePlayer(), but this breaks some tapes */
11319   ScrollPlayer(player, SCROLL_INIT);
11320 #endif
11321
11322   return MP_MOVING;
11323 }
11324
11325 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11326 {
11327   int jx = player->jx, jy = player->jy;
11328   int old_jx = jx, old_jy = jy;
11329   int moved = MP_NO_ACTION;
11330
11331   if (!player->active)
11332     return FALSE;
11333
11334   if (!dx && !dy)
11335   {
11336     if (player->MovPos == 0)
11337     {
11338       player->is_moving = FALSE;
11339       player->is_digging = FALSE;
11340       player->is_collecting = FALSE;
11341       player->is_snapping = FALSE;
11342       player->is_pushing = FALSE;
11343     }
11344
11345     return FALSE;
11346   }
11347
11348   if (player->move_delay > 0)
11349     return FALSE;
11350
11351   player->move_delay = -1;              /* set to "uninitialized" value */
11352
11353   /* store if player is automatically moved to next field */
11354   player->is_auto_moving = (player->programmed_action != MV_NONE);
11355
11356   /* remove the last programmed player action */
11357   player->programmed_action = 0;
11358
11359   if (player->MovPos)
11360   {
11361     /* should only happen if pre-1.2 tape recordings are played */
11362     /* this is only for backward compatibility */
11363
11364     int original_move_delay_value = player->move_delay_value;
11365
11366 #if DEBUG
11367     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11368            tape.counter);
11369 #endif
11370
11371     /* scroll remaining steps with finest movement resolution */
11372     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11373
11374     while (player->MovPos)
11375     {
11376       ScrollPlayer(player, SCROLL_GO_ON);
11377       ScrollScreen(NULL, SCROLL_GO_ON);
11378
11379       AdvanceFrameAndPlayerCounters(player->index_nr);
11380
11381       DrawAllPlayers();
11382       BackToFront();
11383     }
11384
11385     player->move_delay_value = original_move_delay_value;
11386   }
11387
11388   player->is_active = FALSE;
11389
11390   if (player->last_move_dir & MV_HORIZONTAL)
11391   {
11392     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11393       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11394   }
11395   else
11396   {
11397     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11398       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11399   }
11400
11401 #if USE_FIXED_BORDER_RUNNING_GFX
11402   if (!moved && !player->is_active)
11403   {
11404     player->is_moving = FALSE;
11405     player->is_digging = FALSE;
11406     player->is_collecting = FALSE;
11407     player->is_snapping = FALSE;
11408     player->is_pushing = FALSE;
11409   }
11410 #endif
11411
11412   jx = player->jx;
11413   jy = player->jy;
11414
11415 #if 1
11416   if (moved & MP_MOVING && !ScreenMovPos &&
11417       (player->index_nr == game.centered_player_nr ||
11418        game.centered_player_nr == -1))
11419 #else
11420   if (moved & MP_MOVING && !ScreenMovPos &&
11421       (player == local_player || !options.network))
11422 #endif
11423   {
11424     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11425     int offset = (setup.scroll_delay ? 3 : 0);
11426
11427     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11428     {
11429       /* actual player has left the screen -- scroll in that direction */
11430       if (jx != old_jx)         /* player has moved horizontally */
11431         scroll_x += (jx - old_jx);
11432       else                      /* player has moved vertically */
11433         scroll_y += (jy - old_jy);
11434     }
11435     else
11436     {
11437       if (jx != old_jx)         /* player has moved horizontally */
11438       {
11439         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11440             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11441           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11442
11443         /* don't scroll over playfield boundaries */
11444         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11445           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11446
11447         /* don't scroll more than one field at a time */
11448         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11449
11450         /* don't scroll against the player's moving direction */
11451         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11452             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11453           scroll_x = old_scroll_x;
11454       }
11455       else                      /* player has moved vertically */
11456       {
11457         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11458             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11459           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11460
11461         /* don't scroll over playfield boundaries */
11462         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11463           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11464
11465         /* don't scroll more than one field at a time */
11466         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11467
11468         /* don't scroll against the player's moving direction */
11469         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11470             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11471           scroll_y = old_scroll_y;
11472       }
11473     }
11474
11475     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11476     {
11477 #if 1
11478       if (!options.network && game.centered_player_nr == -1 &&
11479           !AllPlayersInVisibleScreen())
11480       {
11481         scroll_x = old_scroll_x;
11482         scroll_y = old_scroll_y;
11483       }
11484       else
11485 #else
11486       if (!options.network && !AllPlayersInVisibleScreen())
11487       {
11488         scroll_x = old_scroll_x;
11489         scroll_y = old_scroll_y;
11490       }
11491       else
11492 #endif
11493       {
11494         ScrollScreen(player, SCROLL_INIT);
11495         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11496       }
11497     }
11498   }
11499
11500   player->StepFrame = 0;
11501
11502   if (moved & MP_MOVING)
11503   {
11504     if (old_jx != jx && old_jy == jy)
11505       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11506     else if (old_jx == jx && old_jy != jy)
11507       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11508
11509     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11510
11511     player->last_move_dir = player->MovDir;
11512     player->is_moving = TRUE;
11513     player->is_snapping = FALSE;
11514     player->is_switching = FALSE;
11515     player->is_dropping = FALSE;
11516     player->is_dropping_pressed = FALSE;
11517     player->drop_pressed_delay = 0;
11518
11519 #if 0
11520     /* should better be called here than above, but this breaks some tapes */
11521     ScrollPlayer(player, SCROLL_INIT);
11522 #endif
11523   }
11524   else
11525   {
11526     CheckGravityMovementWhenNotMoving(player);
11527
11528     player->is_moving = FALSE;
11529
11530     /* at this point, the player is allowed to move, but cannot move right now
11531        (e.g. because of something blocking the way) -- ensure that the player
11532        is also allowed to move in the next frame (in old versions before 3.1.1,
11533        the player was forced to wait again for eight frames before next try) */
11534
11535     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11536       player->move_delay = 0;   /* allow direct movement in the next frame */
11537   }
11538
11539   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11540     player->move_delay = player->move_delay_value;
11541
11542   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11543   {
11544     TestIfPlayerTouchesBadThing(jx, jy);
11545     TestIfPlayerTouchesCustomElement(jx, jy);
11546   }
11547
11548   if (!player->active)
11549     RemovePlayer(player);
11550
11551   return moved;
11552 }
11553
11554 void ScrollPlayer(struct PlayerInfo *player, int mode)
11555 {
11556   int jx = player->jx, jy = player->jy;
11557   int last_jx = player->last_jx, last_jy = player->last_jy;
11558   int move_stepsize = TILEX / player->move_delay_value;
11559
11560 #if USE_NEW_PLAYER_SPEED
11561   if (!player->active)
11562     return;
11563
11564   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11565     return;
11566 #else
11567   if (!player->active || player->MovPos == 0)
11568     return;
11569 #endif
11570
11571   if (mode == SCROLL_INIT)
11572   {
11573     player->actual_frame_counter = FrameCounter;
11574     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11575
11576     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11577         Feld[last_jx][last_jy] == EL_EMPTY)
11578     {
11579       int last_field_block_delay = 0;   /* start with no blocking at all */
11580       int block_delay_adjustment = player->block_delay_adjustment;
11581
11582       /* if player blocks last field, add delay for exactly one move */
11583       if (player->block_last_field)
11584       {
11585         last_field_block_delay += player->move_delay_value;
11586
11587         /* when blocking enabled, prevent moving up despite gravity */
11588 #if USE_PLAYER_GRAVITY
11589         if (player->gravity && player->MovDir == MV_UP)
11590           block_delay_adjustment = -1;
11591 #else
11592         if (game.gravity && player->MovDir == MV_UP)
11593           block_delay_adjustment = -1;
11594 #endif
11595       }
11596
11597       /* add block delay adjustment (also possible when not blocking) */
11598       last_field_block_delay += block_delay_adjustment;
11599
11600       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11601       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11602     }
11603
11604 #if USE_NEW_PLAYER_SPEED
11605     if (player->MovPos != 0)    /* player has not yet reached destination */
11606       return;
11607 #else
11608     return;
11609 #endif
11610   }
11611   else if (!FrameReached(&player->actual_frame_counter, 1))
11612     return;
11613
11614 #if USE_NEW_PLAYER_SPEED
11615   if (player->MovPos != 0)
11616   {
11617     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11618     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11619
11620     /* before DrawPlayer() to draw correct player graphic for this case */
11621     if (player->MovPos == 0)
11622       CheckGravityMovement(player);
11623   }
11624 #else
11625   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11626   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11627
11628   /* before DrawPlayer() to draw correct player graphic for this case */
11629   if (player->MovPos == 0)
11630     CheckGravityMovement(player);
11631 #endif
11632
11633   if (player->MovPos == 0)      /* player reached destination field */
11634   {
11635     if (player->move_delay_reset_counter > 0)
11636     {
11637       player->move_delay_reset_counter--;
11638
11639       if (player->move_delay_reset_counter == 0)
11640       {
11641         /* continue with normal speed after quickly moving through gate */
11642         HALVE_PLAYER_SPEED(player);
11643
11644         /* be able to make the next move without delay */
11645         player->move_delay = 0;
11646       }
11647     }
11648
11649     player->last_jx = jx;
11650     player->last_jy = jy;
11651
11652     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11653         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11654         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11655         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11656         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11657         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11658     {
11659       DrawPlayer(player);       /* needed here only to cleanup last field */
11660       RemovePlayer(player);
11661
11662       if (local_player->friends_still_needed == 0 ||
11663           IS_SP_ELEMENT(Feld[jx][jy]))
11664         PlayerWins(player);
11665     }
11666
11667     /* this breaks one level: "machine", level 000 */
11668     {
11669       int move_direction = player->MovDir;
11670       int enter_side = MV_DIR_OPPOSITE(move_direction);
11671       int leave_side = move_direction;
11672       int old_jx = last_jx;
11673       int old_jy = last_jy;
11674       int old_element = Feld[old_jx][old_jy];
11675       int new_element = Feld[jx][jy];
11676
11677       if (IS_CUSTOM_ELEMENT(old_element))
11678         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11679                                    CE_LEFT_BY_PLAYER,
11680                                    player->index_bit, leave_side);
11681
11682       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11683                                           CE_PLAYER_LEAVES_X,
11684                                           player->index_bit, leave_side);
11685
11686       if (IS_CUSTOM_ELEMENT(new_element))
11687         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11688                                    player->index_bit, enter_side);
11689
11690       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11691                                           CE_PLAYER_ENTERS_X,
11692                                           player->index_bit, enter_side);
11693
11694       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11695                                         CE_MOVE_OF_X, move_direction);
11696     }
11697
11698     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11699     {
11700       TestIfPlayerTouchesBadThing(jx, jy);
11701       TestIfPlayerTouchesCustomElement(jx, jy);
11702
11703       /* needed because pushed element has not yet reached its destination,
11704          so it would trigger a change event at its previous field location */
11705       if (!player->is_pushing)
11706         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11707
11708       if (!player->active)
11709         RemovePlayer(player);
11710     }
11711
11712     if (!local_player->LevelSolved && level.use_step_counter)
11713     {
11714       int i;
11715
11716       TimePlayed++;
11717
11718       if (TimeLeft > 0)
11719       {
11720         TimeLeft--;
11721
11722         if (TimeLeft <= 10 && setup.time_limit)
11723           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11724
11725         DrawGameValue_Time(TimeLeft);
11726
11727         if (!TimeLeft && setup.time_limit)
11728           for (i = 0; i < MAX_PLAYERS; i++)
11729             KillPlayer(&stored_player[i]);
11730       }
11731       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11732         DrawGameValue_Time(TimePlayed);
11733     }
11734
11735     if (tape.single_step && tape.recording && !tape.pausing &&
11736         !player->programmed_action)
11737       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11738   }
11739 }
11740
11741 void ScrollScreen(struct PlayerInfo *player, int mode)
11742 {
11743   static unsigned long screen_frame_counter = 0;
11744
11745   if (mode == SCROLL_INIT)
11746   {
11747     /* set scrolling step size according to actual player's moving speed */
11748     ScrollStepSize = TILEX / player->move_delay_value;
11749
11750     screen_frame_counter = FrameCounter;
11751     ScreenMovDir = player->MovDir;
11752     ScreenMovPos = player->MovPos;
11753     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11754     return;
11755   }
11756   else if (!FrameReached(&screen_frame_counter, 1))
11757     return;
11758
11759   if (ScreenMovPos)
11760   {
11761     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11762     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11763     redraw_mask |= REDRAW_FIELD;
11764   }
11765   else
11766     ScreenMovDir = MV_NONE;
11767 }
11768
11769 void TestIfPlayerTouchesCustomElement(int x, int y)
11770 {
11771   static int xy[4][2] =
11772   {
11773     { 0, -1 },
11774     { -1, 0 },
11775     { +1, 0 },
11776     { 0, +1 }
11777   };
11778   static int trigger_sides[4][2] =
11779   {
11780     /* center side       border side */
11781     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11782     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11783     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11784     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11785   };
11786   static int touch_dir[4] =
11787   {
11788     MV_LEFT | MV_RIGHT,
11789     MV_UP   | MV_DOWN,
11790     MV_UP   | MV_DOWN,
11791     MV_LEFT | MV_RIGHT
11792   };
11793   int center_element = Feld[x][y];      /* should always be non-moving! */
11794   int i;
11795
11796   for (i = 0; i < NUM_DIRECTIONS; i++)
11797   {
11798     int xx = x + xy[i][0];
11799     int yy = y + xy[i][1];
11800     int center_side = trigger_sides[i][0];
11801     int border_side = trigger_sides[i][1];
11802     int border_element;
11803
11804     if (!IN_LEV_FIELD(xx, yy))
11805       continue;
11806
11807     if (IS_PLAYER(x, y))
11808     {
11809       struct PlayerInfo *player = PLAYERINFO(x, y);
11810
11811       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11812         border_element = Feld[xx][yy];          /* may be moving! */
11813       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11814         border_element = Feld[xx][yy];
11815       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11816         border_element = MovingOrBlocked2Element(xx, yy);
11817       else
11818         continue;               /* center and border element do not touch */
11819
11820       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11821                                  player->index_bit, border_side);
11822       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11823                                           CE_PLAYER_TOUCHES_X,
11824                                           player->index_bit, border_side);
11825     }
11826     else if (IS_PLAYER(xx, yy))
11827     {
11828       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11829
11830       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11831       {
11832         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11833           continue;             /* center and border element do not touch */
11834       }
11835
11836       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11837                                  player->index_bit, center_side);
11838       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11839                                           CE_PLAYER_TOUCHES_X,
11840                                           player->index_bit, center_side);
11841       break;
11842     }
11843   }
11844 }
11845
11846 #if USE_ELEMENT_TOUCHING_BUGFIX
11847
11848 void TestIfElementTouchesCustomElement(int x, int y)
11849 {
11850   static int xy[4][2] =
11851   {
11852     { 0, -1 },
11853     { -1, 0 },
11854     { +1, 0 },
11855     { 0, +1 }
11856   };
11857   static int trigger_sides[4][2] =
11858   {
11859     /* center side      border side */
11860     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11861     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11862     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11863     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11864   };
11865   static int touch_dir[4] =
11866   {
11867     MV_LEFT | MV_RIGHT,
11868     MV_UP   | MV_DOWN,
11869     MV_UP   | MV_DOWN,
11870     MV_LEFT | MV_RIGHT
11871   };
11872   boolean change_center_element = FALSE;
11873   int center_element = Feld[x][y];      /* should always be non-moving! */
11874   int border_element_old[NUM_DIRECTIONS];
11875   int i;
11876
11877   for (i = 0; i < NUM_DIRECTIONS; i++)
11878   {
11879     int xx = x + xy[i][0];
11880     int yy = y + xy[i][1];
11881     int border_element;
11882
11883     border_element_old[i] = -1;
11884
11885     if (!IN_LEV_FIELD(xx, yy))
11886       continue;
11887
11888     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11889       border_element = Feld[xx][yy];    /* may be moving! */
11890     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11891       border_element = Feld[xx][yy];
11892     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11893       border_element = MovingOrBlocked2Element(xx, yy);
11894     else
11895       continue;                 /* center and border element do not touch */
11896
11897     border_element_old[i] = border_element;
11898   }
11899
11900   for (i = 0; i < NUM_DIRECTIONS; i++)
11901   {
11902     int xx = x + xy[i][0];
11903     int yy = y + xy[i][1];
11904     int center_side = trigger_sides[i][0];
11905     int border_element = border_element_old[i];
11906
11907     if (border_element == -1)
11908       continue;
11909
11910     /* check for change of border element */
11911     CheckElementChangeBySide(xx, yy, border_element, center_element,
11912                              CE_TOUCHING_X, center_side);
11913   }
11914
11915   for (i = 0; i < NUM_DIRECTIONS; i++)
11916   {
11917     int border_side = trigger_sides[i][1];
11918     int border_element = border_element_old[i];
11919
11920     if (border_element == -1)
11921       continue;
11922
11923     /* check for change of center element (but change it only once) */
11924     if (!change_center_element)
11925       change_center_element =
11926         CheckElementChangeBySide(x, y, center_element, border_element,
11927                                  CE_TOUCHING_X, border_side);
11928   }
11929 }
11930
11931 #else
11932
11933 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11934 {
11935   static int xy[4][2] =
11936   {
11937     { 0, -1 },
11938     { -1, 0 },
11939     { +1, 0 },
11940     { 0, +1 }
11941   };
11942   static int trigger_sides[4][2] =
11943   {
11944     /* center side      border side */
11945     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11946     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11947     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11948     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11949   };
11950   static int touch_dir[4] =
11951   {
11952     MV_LEFT | MV_RIGHT,
11953     MV_UP   | MV_DOWN,
11954     MV_UP   | MV_DOWN,
11955     MV_LEFT | MV_RIGHT
11956   };
11957   boolean change_center_element = FALSE;
11958   int center_element = Feld[x][y];      /* should always be non-moving! */
11959   int i;
11960
11961   for (i = 0; i < NUM_DIRECTIONS; i++)
11962   {
11963     int xx = x + xy[i][0];
11964     int yy = y + xy[i][1];
11965     int center_side = trigger_sides[i][0];
11966     int border_side = trigger_sides[i][1];
11967     int border_element;
11968
11969     if (!IN_LEV_FIELD(xx, yy))
11970       continue;
11971
11972     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11973       border_element = Feld[xx][yy];    /* may be moving! */
11974     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11975       border_element = Feld[xx][yy];
11976     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11977       border_element = MovingOrBlocked2Element(xx, yy);
11978     else
11979       continue;                 /* center and border element do not touch */
11980
11981     /* check for change of center element (but change it only once) */
11982     if (!change_center_element)
11983       change_center_element =
11984         CheckElementChangeBySide(x, y, center_element, border_element,
11985                                  CE_TOUCHING_X, border_side);
11986
11987     /* check for change of border element */
11988     CheckElementChangeBySide(xx, yy, border_element, center_element,
11989                              CE_TOUCHING_X, center_side);
11990   }
11991 }
11992
11993 #endif
11994
11995 void TestIfElementHitsCustomElement(int x, int y, int direction)
11996 {
11997   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11998   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11999   int hitx = x + dx, hity = y + dy;
12000   int hitting_element = Feld[x][y];
12001   int touched_element;
12002
12003   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12004     return;
12005
12006   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12007                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12008
12009   if (IN_LEV_FIELD(hitx, hity))
12010   {
12011     int opposite_direction = MV_DIR_OPPOSITE(direction);
12012     int hitting_side = direction;
12013     int touched_side = opposite_direction;
12014     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12015                           MovDir[hitx][hity] != direction ||
12016                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12017
12018     object_hit = TRUE;
12019
12020     if (object_hit)
12021     {
12022       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12023                                CE_HITTING_X, touched_side);
12024
12025       CheckElementChangeBySide(hitx, hity, touched_element,
12026                                hitting_element, CE_HIT_BY_X, hitting_side);
12027
12028       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12029                                CE_HIT_BY_SOMETHING, opposite_direction);
12030     }
12031   }
12032
12033   /* "hitting something" is also true when hitting the playfield border */
12034   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12035                            CE_HITTING_SOMETHING, direction);
12036 }
12037
12038 #if 0
12039 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12040 {
12041   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12042   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12043   int hitx = x + dx, hity = y + dy;
12044   int hitting_element = Feld[x][y];
12045   int touched_element;
12046 #if 0
12047   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12048                         !IS_FREE(hitx, hity) &&
12049                         (!IS_MOVING(hitx, hity) ||
12050                          MovDir[hitx][hity] != direction ||
12051                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12052 #endif
12053
12054   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12055     return;
12056
12057 #if 0
12058   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12059     return;
12060 #endif
12061
12062   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12063                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12064
12065   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12066                            EP_CAN_SMASH_EVERYTHING, direction);
12067
12068   if (IN_LEV_FIELD(hitx, hity))
12069   {
12070     int opposite_direction = MV_DIR_OPPOSITE(direction);
12071     int hitting_side = direction;
12072     int touched_side = opposite_direction;
12073 #if 0
12074     int touched_element = MovingOrBlocked2Element(hitx, hity);
12075 #endif
12076 #if 1
12077     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12078                           MovDir[hitx][hity] != direction ||
12079                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12080
12081     object_hit = TRUE;
12082 #endif
12083
12084     if (object_hit)
12085     {
12086       int i;
12087
12088       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12089                                CE_SMASHED_BY_SOMETHING, opposite_direction);
12090
12091       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12092                                CE_OTHER_IS_SMASHING, touched_side);
12093
12094       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12095                                CE_OTHER_GETS_SMASHED, hitting_side);
12096     }
12097   }
12098 }
12099 #endif
12100
12101 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12102 {
12103   int i, kill_x = -1, kill_y = -1;
12104
12105   int bad_element = -1;
12106   static int test_xy[4][2] =
12107   {
12108     { 0, -1 },
12109     { -1, 0 },
12110     { +1, 0 },
12111     { 0, +1 }
12112   };
12113   static int test_dir[4] =
12114   {
12115     MV_UP,
12116     MV_LEFT,
12117     MV_RIGHT,
12118     MV_DOWN
12119   };
12120
12121   for (i = 0; i < NUM_DIRECTIONS; i++)
12122   {
12123     int test_x, test_y, test_move_dir, test_element;
12124
12125     test_x = good_x + test_xy[i][0];
12126     test_y = good_y + test_xy[i][1];
12127
12128     if (!IN_LEV_FIELD(test_x, test_y))
12129       continue;
12130
12131     test_move_dir =
12132       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12133
12134     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12135
12136     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12137        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12138     */
12139     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12140         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12141     {
12142       kill_x = test_x;
12143       kill_y = test_y;
12144       bad_element = test_element;
12145
12146       break;
12147     }
12148   }
12149
12150   if (kill_x != -1 || kill_y != -1)
12151   {
12152     if (IS_PLAYER(good_x, good_y))
12153     {
12154       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12155
12156       if (player->shield_deadly_time_left > 0 &&
12157           !IS_INDESTRUCTIBLE(bad_element))
12158         Bang(kill_x, kill_y);
12159       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12160         KillPlayer(player);
12161     }
12162     else
12163       Bang(good_x, good_y);
12164   }
12165 }
12166
12167 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12168 {
12169   int i, kill_x = -1, kill_y = -1;
12170   int bad_element = Feld[bad_x][bad_y];
12171   static int test_xy[4][2] =
12172   {
12173     { 0, -1 },
12174     { -1, 0 },
12175     { +1, 0 },
12176     { 0, +1 }
12177   };
12178   static int touch_dir[4] =
12179   {
12180     MV_LEFT | MV_RIGHT,
12181     MV_UP   | MV_DOWN,
12182     MV_UP   | MV_DOWN,
12183     MV_LEFT | MV_RIGHT
12184   };
12185   static int test_dir[4] =
12186   {
12187     MV_UP,
12188     MV_LEFT,
12189     MV_RIGHT,
12190     MV_DOWN
12191   };
12192
12193   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12194     return;
12195
12196   for (i = 0; i < NUM_DIRECTIONS; i++)
12197   {
12198     int test_x, test_y, test_move_dir, test_element;
12199
12200     test_x = bad_x + test_xy[i][0];
12201     test_y = bad_y + test_xy[i][1];
12202     if (!IN_LEV_FIELD(test_x, test_y))
12203       continue;
12204
12205     test_move_dir =
12206       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12207
12208     test_element = Feld[test_x][test_y];
12209
12210     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12211        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12212     */
12213     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12214         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12215     {
12216       /* good thing is player or penguin that does not move away */
12217       if (IS_PLAYER(test_x, test_y))
12218       {
12219         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12220
12221         if (bad_element == EL_ROBOT && player->is_moving)
12222           continue;     /* robot does not kill player if he is moving */
12223
12224         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12225         {
12226           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12227             continue;           /* center and border element do not touch */
12228         }
12229
12230         kill_x = test_x;
12231         kill_y = test_y;
12232         break;
12233       }
12234       else if (test_element == EL_PENGUIN)
12235       {
12236         kill_x = test_x;
12237         kill_y = test_y;
12238         break;
12239       }
12240     }
12241   }
12242
12243   if (kill_x != -1 || kill_y != -1)
12244   {
12245     if (IS_PLAYER(kill_x, kill_y))
12246     {
12247       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12248
12249       if (player->shield_deadly_time_left > 0 &&
12250           !IS_INDESTRUCTIBLE(bad_element))
12251         Bang(bad_x, bad_y);
12252       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12253         KillPlayer(player);
12254     }
12255     else
12256       Bang(kill_x, kill_y);
12257   }
12258 }
12259
12260 void TestIfPlayerTouchesBadThing(int x, int y)
12261 {
12262   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12263 }
12264
12265 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12266 {
12267   TestIfGoodThingHitsBadThing(x, y, move_dir);
12268 }
12269
12270 void TestIfBadThingTouchesPlayer(int x, int y)
12271 {
12272   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12273 }
12274
12275 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12276 {
12277   TestIfBadThingHitsGoodThing(x, y, move_dir);
12278 }
12279
12280 void TestIfFriendTouchesBadThing(int x, int y)
12281 {
12282   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12283 }
12284
12285 void TestIfBadThingTouchesFriend(int x, int y)
12286 {
12287   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12288 }
12289
12290 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12291 {
12292   int i, kill_x = bad_x, kill_y = bad_y;
12293   static int xy[4][2] =
12294   {
12295     { 0, -1 },
12296     { -1, 0 },
12297     { +1, 0 },
12298     { 0, +1 }
12299   };
12300
12301   for (i = 0; i < NUM_DIRECTIONS; i++)
12302   {
12303     int x, y, element;
12304
12305     x = bad_x + xy[i][0];
12306     y = bad_y + xy[i][1];
12307     if (!IN_LEV_FIELD(x, y))
12308       continue;
12309
12310     element = Feld[x][y];
12311     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12312         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12313     {
12314       kill_x = x;
12315       kill_y = y;
12316       break;
12317     }
12318   }
12319
12320   if (kill_x != bad_x || kill_y != bad_y)
12321     Bang(bad_x, bad_y);
12322 }
12323
12324 void KillPlayer(struct PlayerInfo *player)
12325 {
12326   int jx = player->jx, jy = player->jy;
12327
12328   if (!player->active)
12329     return;
12330
12331   /* the following code was introduced to prevent an infinite loop when calling
12332      -> Bang()
12333      -> CheckTriggeredElementChangeExt()
12334      -> ExecuteCustomElementAction()
12335      -> KillPlayer()
12336      -> (infinitely repeating the above sequence of function calls)
12337      which occurs when killing the player while having a CE with the setting
12338      "kill player X when explosion of <player X>"; the solution using a new
12339      field "player->killed" was chosen for backwards compatibility, although
12340      clever use of the fields "player->active" etc. would probably also work */
12341 #if 1
12342   if (player->killed)
12343     return;
12344 #endif
12345
12346   player->killed = TRUE;
12347
12348   /* remove accessible field at the player's position */
12349   Feld[jx][jy] = EL_EMPTY;
12350
12351   /* deactivate shield (else Bang()/Explode() would not work right) */
12352   player->shield_normal_time_left = 0;
12353   player->shield_deadly_time_left = 0;
12354
12355   Bang(jx, jy);
12356   BuryPlayer(player);
12357 }
12358
12359 static void KillPlayerUnlessEnemyProtected(int x, int y)
12360 {
12361   if (!PLAYER_ENEMY_PROTECTED(x, y))
12362     KillPlayer(PLAYERINFO(x, y));
12363 }
12364
12365 static void KillPlayerUnlessExplosionProtected(int x, int y)
12366 {
12367   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12368     KillPlayer(PLAYERINFO(x, y));
12369 }
12370
12371 void BuryPlayer(struct PlayerInfo *player)
12372 {
12373   int jx = player->jx, jy = player->jy;
12374
12375   if (!player->active)
12376     return;
12377
12378   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12379   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12380
12381   player->GameOver = TRUE;
12382   RemovePlayer(player);
12383 }
12384
12385 void RemovePlayer(struct PlayerInfo *player)
12386 {
12387   int jx = player->jx, jy = player->jy;
12388   int i, found = FALSE;
12389
12390   player->present = FALSE;
12391   player->active = FALSE;
12392
12393   if (!ExplodeField[jx][jy])
12394     StorePlayer[jx][jy] = 0;
12395
12396   if (player->is_moving)
12397     DrawLevelField(player->last_jx, player->last_jy);
12398
12399   for (i = 0; i < MAX_PLAYERS; i++)
12400     if (stored_player[i].active)
12401       found = TRUE;
12402
12403   if (!found)
12404     AllPlayersGone = TRUE;
12405
12406   ExitX = ZX = jx;
12407   ExitY = ZY = jy;
12408 }
12409
12410 #if USE_NEW_SNAP_DELAY
12411 static void setFieldForSnapping(int x, int y, int element, int direction)
12412 {
12413   struct ElementInfo *ei = &element_info[element];
12414   int direction_bit = MV_DIR_TO_BIT(direction);
12415   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12416   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12417                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12418
12419   Feld[x][y] = EL_ELEMENT_SNAPPING;
12420   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12421
12422   ResetGfxAnimation(x, y);
12423
12424   GfxElement[x][y] = element;
12425   GfxAction[x][y] = action;
12426   GfxDir[x][y] = direction;
12427   GfxFrame[x][y] = -1;
12428 }
12429 #endif
12430
12431 /*
12432   =============================================================================
12433   checkDiagonalPushing()
12434   -----------------------------------------------------------------------------
12435   check if diagonal input device direction results in pushing of object
12436   (by checking if the alternative direction is walkable, diggable, ...)
12437   =============================================================================
12438 */
12439
12440 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12441                                     int x, int y, int real_dx, int real_dy)
12442 {
12443   int jx, jy, dx, dy, xx, yy;
12444
12445   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12446     return TRUE;
12447
12448   /* diagonal direction: check alternative direction */
12449   jx = player->jx;
12450   jy = player->jy;
12451   dx = x - jx;
12452   dy = y - jy;
12453   xx = jx + (dx == 0 ? real_dx : 0);
12454   yy = jy + (dy == 0 ? real_dy : 0);
12455
12456   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12457 }
12458
12459 /*
12460   =============================================================================
12461   DigField()
12462   -----------------------------------------------------------------------------
12463   x, y:                 field next to player (non-diagonal) to try to dig to
12464   real_dx, real_dy:     direction as read from input device (can be diagonal)
12465   =============================================================================
12466 */
12467
12468 int DigField(struct PlayerInfo *player,
12469              int oldx, int oldy, int x, int y,
12470              int real_dx, int real_dy, int mode)
12471 {
12472   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12473   boolean player_was_pushing = player->is_pushing;
12474   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12475   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12476   int jx = oldx, jy = oldy;
12477   int dx = x - jx, dy = y - jy;
12478   int nextx = x + dx, nexty = y + dy;
12479   int move_direction = (dx == -1 ? MV_LEFT  :
12480                         dx == +1 ? MV_RIGHT :
12481                         dy == -1 ? MV_UP    :
12482                         dy == +1 ? MV_DOWN  : MV_NONE);
12483   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12484   int dig_side = MV_DIR_OPPOSITE(move_direction);
12485   int old_element = Feld[jx][jy];
12486 #if USE_FIXED_DONT_RUN_INTO
12487   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12488 #else
12489   int element;
12490 #endif
12491   int collect_count;
12492
12493   if (is_player)                /* function can also be called by EL_PENGUIN */
12494   {
12495     if (player->MovPos == 0)
12496     {
12497       player->is_digging = FALSE;
12498       player->is_collecting = FALSE;
12499     }
12500
12501     if (player->MovPos == 0)    /* last pushing move finished */
12502       player->is_pushing = FALSE;
12503
12504     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12505     {
12506       player->is_switching = FALSE;
12507       player->push_delay = -1;
12508
12509       return MP_NO_ACTION;
12510     }
12511   }
12512
12513 #if !USE_FIXED_DONT_RUN_INTO
12514   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12515     return MP_NO_ACTION;
12516 #endif
12517
12518   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12519     old_element = Back[jx][jy];
12520
12521   /* in case of element dropped at player position, check background */
12522   else if (Back[jx][jy] != EL_EMPTY &&
12523            game.engine_version >= VERSION_IDENT(2,2,0,0))
12524     old_element = Back[jx][jy];
12525
12526   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12527     return MP_NO_ACTION;        /* field has no opening in this direction */
12528
12529   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12530     return MP_NO_ACTION;        /* field has no opening in this direction */
12531
12532 #if USE_FIXED_DONT_RUN_INTO
12533   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12534   {
12535     SplashAcid(x, y);
12536
12537     Feld[jx][jy] = player->artwork_element;
12538     InitMovingField(jx, jy, MV_DOWN);
12539     Store[jx][jy] = EL_ACID;
12540     ContinueMoving(jx, jy);
12541     BuryPlayer(player);
12542
12543     return MP_DONT_RUN_INTO;
12544   }
12545 #endif
12546
12547 #if USE_FIXED_DONT_RUN_INTO
12548   if (player_can_move && DONT_RUN_INTO(element))
12549   {
12550     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12551
12552     return MP_DONT_RUN_INTO;
12553   }
12554 #endif
12555
12556 #if USE_FIXED_DONT_RUN_INTO
12557   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12558     return MP_NO_ACTION;
12559 #endif
12560
12561 #if !USE_FIXED_DONT_RUN_INTO
12562   element = Feld[x][y];
12563 #endif
12564
12565   collect_count = element_info[element].collect_count_initial;
12566
12567   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12568     return MP_NO_ACTION;
12569
12570   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12571     player_can_move = player_can_move_or_snap;
12572
12573   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12574       game.engine_version >= VERSION_IDENT(2,2,0,0))
12575   {
12576     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12577                                player->index_bit, dig_side);
12578     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12579                                         player->index_bit, dig_side);
12580
12581     if (element == EL_DC_LANDMINE)
12582       Bang(x, y);
12583
12584     if (Feld[x][y] != element)          /* field changed by snapping */
12585       return MP_ACTION;
12586
12587     return MP_NO_ACTION;
12588   }
12589
12590 #if USE_PLAYER_GRAVITY
12591   if (player->gravity && is_player && !player->is_auto_moving &&
12592       canFallDown(player) && move_direction != MV_DOWN &&
12593       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12594     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12595 #else
12596   if (game.gravity && is_player && !player->is_auto_moving &&
12597       canFallDown(player) && move_direction != MV_DOWN &&
12598       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12599     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12600 #endif
12601
12602   if (player_can_move &&
12603       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12604   {
12605     int sound_element = SND_ELEMENT(element);
12606     int sound_action = ACTION_WALKING;
12607
12608     if (IS_RND_GATE(element))
12609     {
12610       if (!player->key[RND_GATE_NR(element)])
12611         return MP_NO_ACTION;
12612     }
12613     else if (IS_RND_GATE_GRAY(element))
12614     {
12615       if (!player->key[RND_GATE_GRAY_NR(element)])
12616         return MP_NO_ACTION;
12617     }
12618     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12619     {
12620       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12621         return MP_NO_ACTION;
12622     }
12623     else if (element == EL_EXIT_OPEN ||
12624              element == EL_EM_EXIT_OPEN ||
12625              element == EL_STEEL_EXIT_OPEN ||
12626              element == EL_EM_STEEL_EXIT_OPEN ||
12627              element == EL_SP_EXIT_OPEN ||
12628              element == EL_SP_EXIT_OPENING)
12629     {
12630       sound_action = ACTION_PASSING;    /* player is passing exit */
12631     }
12632     else if (element == EL_EMPTY)
12633     {
12634       sound_action = ACTION_MOVING;             /* nothing to walk on */
12635     }
12636
12637     /* play sound from background or player, whatever is available */
12638     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12639       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12640     else
12641       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12642   }
12643   else if (player_can_move &&
12644            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12645   {
12646     if (!ACCESS_FROM(element, opposite_direction))
12647       return MP_NO_ACTION;      /* field not accessible from this direction */
12648
12649     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12650       return MP_NO_ACTION;
12651
12652     if (IS_EM_GATE(element))
12653     {
12654       if (!player->key[EM_GATE_NR(element)])
12655         return MP_NO_ACTION;
12656     }
12657     else if (IS_EM_GATE_GRAY(element))
12658     {
12659       if (!player->key[EM_GATE_GRAY_NR(element)])
12660         return MP_NO_ACTION;
12661     }
12662     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12663     {
12664       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12665         return MP_NO_ACTION;
12666     }
12667     else if (IS_EMC_GATE(element))
12668     {
12669       if (!player->key[EMC_GATE_NR(element)])
12670         return MP_NO_ACTION;
12671     }
12672     else if (IS_EMC_GATE_GRAY(element))
12673     {
12674       if (!player->key[EMC_GATE_GRAY_NR(element)])
12675         return MP_NO_ACTION;
12676     }
12677     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12678     {
12679       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12680         return MP_NO_ACTION;
12681     }
12682     else if (element == EL_DC_GATE_WHITE ||
12683              element == EL_DC_GATE_WHITE_GRAY ||
12684              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12685     {
12686       if (player->num_white_keys == 0)
12687         return MP_NO_ACTION;
12688
12689       player->num_white_keys--;
12690     }
12691     else if (IS_SP_PORT(element))
12692     {
12693       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12694           element == EL_SP_GRAVITY_PORT_RIGHT ||
12695           element == EL_SP_GRAVITY_PORT_UP ||
12696           element == EL_SP_GRAVITY_PORT_DOWN)
12697 #if USE_PLAYER_GRAVITY
12698         player->gravity = !player->gravity;
12699 #else
12700         game.gravity = !game.gravity;
12701 #endif
12702       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12703                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12704                element == EL_SP_GRAVITY_ON_PORT_UP ||
12705                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12706 #if USE_PLAYER_GRAVITY
12707         player->gravity = TRUE;
12708 #else
12709         game.gravity = TRUE;
12710 #endif
12711       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12712                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12713                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12714                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12715 #if USE_PLAYER_GRAVITY
12716         player->gravity = FALSE;
12717 #else
12718         game.gravity = FALSE;
12719 #endif
12720     }
12721
12722     /* automatically move to the next field with double speed */
12723     player->programmed_action = move_direction;
12724
12725     if (player->move_delay_reset_counter == 0)
12726     {
12727       player->move_delay_reset_counter = 2;     /* two double speed steps */
12728
12729       DOUBLE_PLAYER_SPEED(player);
12730     }
12731
12732     PlayLevelSoundAction(x, y, ACTION_PASSING);
12733   }
12734   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12735   {
12736     RemoveField(x, y);
12737
12738     if (mode != DF_SNAP)
12739     {
12740       GfxElement[x][y] = GFX_ELEMENT(element);
12741       player->is_digging = TRUE;
12742     }
12743
12744     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12745
12746     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12747                                         player->index_bit, dig_side);
12748
12749     if (mode == DF_SNAP)
12750     {
12751 #if USE_NEW_SNAP_DELAY
12752       if (level.block_snap_field)
12753         setFieldForSnapping(x, y, element, move_direction);
12754       else
12755         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12756 #else
12757       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12758 #endif
12759
12760       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12761                                           player->index_bit, dig_side);
12762     }
12763   }
12764   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12765   {
12766     RemoveField(x, y);
12767
12768     if (is_player && mode != DF_SNAP)
12769     {
12770       GfxElement[x][y] = element;
12771       player->is_collecting = TRUE;
12772     }
12773
12774     if (element == EL_SPEED_PILL)
12775     {
12776       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12777     }
12778     else if (element == EL_EXTRA_TIME && level.time > 0)
12779     {
12780       TimeLeft += level.extra_time;
12781       DrawGameValue_Time(TimeLeft);
12782     }
12783     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12784     {
12785       player->shield_normal_time_left += level.shield_normal_time;
12786       if (element == EL_SHIELD_DEADLY)
12787         player->shield_deadly_time_left += level.shield_deadly_time;
12788     }
12789     else if (element == EL_DYNAMITE ||
12790              element == EL_EM_DYNAMITE ||
12791              element == EL_SP_DISK_RED)
12792     {
12793       if (player->inventory_size < MAX_INVENTORY_SIZE)
12794         player->inventory_element[player->inventory_size++] = element;
12795
12796       DrawGameDoorValues();
12797     }
12798     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12799     {
12800       player->dynabomb_count++;
12801       player->dynabombs_left++;
12802     }
12803     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12804     {
12805       player->dynabomb_size++;
12806     }
12807     else if (element == EL_DYNABOMB_INCREASE_POWER)
12808     {
12809       player->dynabomb_xl = TRUE;
12810     }
12811     else if (IS_KEY(element))
12812     {
12813       player->key[KEY_NR(element)] = TRUE;
12814
12815       DrawGameDoorValues();
12816     }
12817     else if (element == EL_DC_KEY_WHITE)
12818     {
12819       player->num_white_keys++;
12820
12821       /* display white keys? */
12822       /* DrawGameDoorValues(); */
12823     }
12824     else if (IS_ENVELOPE(element))
12825     {
12826       player->show_envelope = element;
12827     }
12828     else if (element == EL_EMC_LENSES)
12829     {
12830       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12831
12832       RedrawAllInvisibleElementsForLenses();
12833     }
12834     else if (element == EL_EMC_MAGNIFIER)
12835     {
12836       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12837
12838       RedrawAllInvisibleElementsForMagnifier();
12839     }
12840     else if (IS_DROPPABLE(element) ||
12841              IS_THROWABLE(element))     /* can be collected and dropped */
12842     {
12843       int i;
12844
12845       if (collect_count == 0)
12846         player->inventory_infinite_element = element;
12847       else
12848         for (i = 0; i < collect_count; i++)
12849           if (player->inventory_size < MAX_INVENTORY_SIZE)
12850             player->inventory_element[player->inventory_size++] = element;
12851
12852       DrawGameDoorValues();
12853     }
12854     else if (collect_count > 0)
12855     {
12856       local_player->gems_still_needed -= collect_count;
12857       if (local_player->gems_still_needed < 0)
12858         local_player->gems_still_needed = 0;
12859
12860       DrawGameValue_Emeralds(local_player->gems_still_needed);
12861     }
12862
12863     RaiseScoreElement(element);
12864     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12865
12866     if (is_player)
12867       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12868                                           player->index_bit, dig_side);
12869
12870     if (mode == DF_SNAP)
12871     {
12872 #if USE_NEW_SNAP_DELAY
12873       if (level.block_snap_field)
12874         setFieldForSnapping(x, y, element, move_direction);
12875       else
12876         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12877 #else
12878       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12879 #endif
12880
12881       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12882                                           player->index_bit, dig_side);
12883     }
12884   }
12885   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12886   {
12887     if (mode == DF_SNAP && element != EL_BD_ROCK)
12888       return MP_NO_ACTION;
12889
12890     if (CAN_FALL(element) && dy)
12891       return MP_NO_ACTION;
12892
12893     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12894         !(element == EL_SPRING && level.use_spring_bug))
12895       return MP_NO_ACTION;
12896
12897     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12898         ((move_direction & MV_VERTICAL &&
12899           ((element_info[element].move_pattern & MV_LEFT &&
12900             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12901            (element_info[element].move_pattern & MV_RIGHT &&
12902             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12903          (move_direction & MV_HORIZONTAL &&
12904           ((element_info[element].move_pattern & MV_UP &&
12905             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12906            (element_info[element].move_pattern & MV_DOWN &&
12907             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12908       return MP_NO_ACTION;
12909
12910     /* do not push elements already moving away faster than player */
12911     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12912         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12913       return MP_NO_ACTION;
12914
12915     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12916     {
12917       if (player->push_delay_value == -1 || !player_was_pushing)
12918         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12919     }
12920     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12921     {
12922       if (player->push_delay_value == -1)
12923         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12924     }
12925     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12926     {
12927       if (!player->is_pushing)
12928         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12929     }
12930
12931     player->is_pushing = TRUE;
12932     player->is_active = TRUE;
12933
12934     if (!(IN_LEV_FIELD(nextx, nexty) &&
12935           (IS_FREE(nextx, nexty) ||
12936            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12937             IS_SB_ELEMENT(element)))))
12938       return MP_NO_ACTION;
12939
12940     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12941       return MP_NO_ACTION;
12942
12943     if (player->push_delay == -1)       /* new pushing; restart delay */
12944       player->push_delay = 0;
12945
12946     if (player->push_delay < player->push_delay_value &&
12947         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12948         element != EL_SPRING && element != EL_BALLOON)
12949     {
12950       /* make sure that there is no move delay before next try to push */
12951       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12952         player->move_delay = 0;
12953
12954       return MP_NO_ACTION;
12955     }
12956
12957     if (IS_SB_ELEMENT(element))
12958     {
12959       if (element == EL_SOKOBAN_FIELD_FULL)
12960       {
12961         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12962         local_player->sokobanfields_still_needed++;
12963       }
12964
12965       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12966       {
12967         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12968         local_player->sokobanfields_still_needed--;
12969       }
12970
12971       Feld[x][y] = EL_SOKOBAN_OBJECT;
12972
12973       if (Back[x][y] == Back[nextx][nexty])
12974         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12975       else if (Back[x][y] != 0)
12976         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12977                                     ACTION_EMPTYING);
12978       else
12979         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12980                                     ACTION_FILLING);
12981
12982       if (local_player->sokobanfields_still_needed == 0 &&
12983           game.emulation == EMU_SOKOBAN)
12984       {
12985         PlayerWins(player);
12986
12987         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12988       }
12989     }
12990     else
12991       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12992
12993     InitMovingField(x, y, move_direction);
12994     GfxAction[x][y] = ACTION_PUSHING;
12995
12996     if (mode == DF_SNAP)
12997       ContinueMoving(x, y);
12998     else
12999       MovPos[x][y] = (dx != 0 ? dx : dy);
13000
13001     Pushed[x][y] = TRUE;
13002     Pushed[nextx][nexty] = TRUE;
13003
13004     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13005       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13006     else
13007       player->push_delay_value = -1;    /* get new value later */
13008
13009     /* check for element change _after_ element has been pushed */
13010     if (game.use_change_when_pushing_bug)
13011     {
13012       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13013                                  player->index_bit, dig_side);
13014       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13015                                           player->index_bit, dig_side);
13016     }
13017   }
13018   else if (IS_SWITCHABLE(element))
13019   {
13020     if (PLAYER_SWITCHING(player, x, y))
13021     {
13022       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13023                                           player->index_bit, dig_side);
13024
13025       return MP_ACTION;
13026     }
13027
13028     player->is_switching = TRUE;
13029     player->switch_x = x;
13030     player->switch_y = y;
13031
13032     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13033
13034     if (element == EL_ROBOT_WHEEL)
13035     {
13036       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13037       ZX = x;
13038       ZY = y;
13039
13040       DrawLevelField(x, y);
13041     }
13042     else if (element == EL_SP_TERMINAL)
13043     {
13044       int xx, yy;
13045
13046       SCAN_PLAYFIELD(xx, yy)
13047       {
13048         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13049           Bang(xx, yy);
13050         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13051           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13052       }
13053     }
13054     else if (IS_BELT_SWITCH(element))
13055     {
13056       ToggleBeltSwitch(x, y);
13057     }
13058     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13059              element == EL_SWITCHGATE_SWITCH_DOWN ||
13060              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13061              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13062     {
13063       ToggleSwitchgateSwitch(x, y);
13064     }
13065     else if (element == EL_LIGHT_SWITCH ||
13066              element == EL_LIGHT_SWITCH_ACTIVE)
13067     {
13068       ToggleLightSwitch(x, y);
13069     }
13070     else if (element == EL_TIMEGATE_SWITCH ||
13071              element == EL_DC_TIMEGATE_SWITCH)
13072     {
13073       ActivateTimegateSwitch(x, y);
13074     }
13075     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13076              element == EL_BALLOON_SWITCH_RIGHT ||
13077              element == EL_BALLOON_SWITCH_UP    ||
13078              element == EL_BALLOON_SWITCH_DOWN  ||
13079              element == EL_BALLOON_SWITCH_NONE  ||
13080              element == EL_BALLOON_SWITCH_ANY)
13081     {
13082       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13083                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13084                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13085                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13086                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13087                              move_direction);
13088     }
13089     else if (element == EL_LAMP)
13090     {
13091       Feld[x][y] = EL_LAMP_ACTIVE;
13092       local_player->lights_still_needed--;
13093
13094       ResetGfxAnimation(x, y);
13095       DrawLevelField(x, y);
13096     }
13097     else if (element == EL_TIME_ORB_FULL)
13098     {
13099       Feld[x][y] = EL_TIME_ORB_EMPTY;
13100
13101       if (level.time > 0 || level.use_time_orb_bug)
13102       {
13103         TimeLeft += level.time_orb_time;
13104         DrawGameValue_Time(TimeLeft);
13105       }
13106
13107       ResetGfxAnimation(x, y);
13108       DrawLevelField(x, y);
13109     }
13110     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13111              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13112     {
13113       int xx, yy;
13114
13115       game.ball_state = !game.ball_state;
13116
13117       SCAN_PLAYFIELD(xx, yy)
13118       {
13119         int e = Feld[xx][yy];
13120
13121         if (game.ball_state)
13122         {
13123           if (e == EL_EMC_MAGIC_BALL)
13124             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13125           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13126             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13127         }
13128         else
13129         {
13130           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13131             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13132           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13133             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13134         }
13135       }
13136     }
13137
13138     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13139                                         player->index_bit, dig_side);
13140
13141     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13142                                         player->index_bit, dig_side);
13143
13144     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13145                                         player->index_bit, dig_side);
13146
13147     return MP_ACTION;
13148   }
13149   else
13150   {
13151     if (!PLAYER_SWITCHING(player, x, y))
13152     {
13153       player->is_switching = TRUE;
13154       player->switch_x = x;
13155       player->switch_y = y;
13156
13157       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13158                                  player->index_bit, dig_side);
13159       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13160                                           player->index_bit, dig_side);
13161
13162       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13163                                  player->index_bit, dig_side);
13164       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13165                                           player->index_bit, dig_side);
13166     }
13167
13168     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13169                                player->index_bit, dig_side);
13170     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13171                                         player->index_bit, dig_side);
13172
13173     return MP_NO_ACTION;
13174   }
13175
13176   player->push_delay = -1;
13177
13178   if (is_player)                /* function can also be called by EL_PENGUIN */
13179   {
13180     if (Feld[x][y] != element)          /* really digged/collected something */
13181     {
13182       player->is_collecting = !player->is_digging;
13183       player->is_active = TRUE;
13184     }
13185   }
13186
13187   return MP_MOVING;
13188 }
13189
13190 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13191 {
13192   int jx = player->jx, jy = player->jy;
13193   int x = jx + dx, y = jy + dy;
13194   int snap_direction = (dx == -1 ? MV_LEFT  :
13195                         dx == +1 ? MV_RIGHT :
13196                         dy == -1 ? MV_UP    :
13197                         dy == +1 ? MV_DOWN  : MV_NONE);
13198   boolean can_continue_snapping = (level.continuous_snapping &&
13199                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13200
13201   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13202     return FALSE;
13203
13204   if (!player->active || !IN_LEV_FIELD(x, y))
13205     return FALSE;
13206
13207   if (dx && dy)
13208     return FALSE;
13209
13210   if (!dx && !dy)
13211   {
13212     if (player->MovPos == 0)
13213       player->is_pushing = FALSE;
13214
13215     player->is_snapping = FALSE;
13216
13217     if (player->MovPos == 0)
13218     {
13219       player->is_moving = FALSE;
13220       player->is_digging = FALSE;
13221       player->is_collecting = FALSE;
13222     }
13223
13224     return FALSE;
13225   }
13226
13227 #if USE_NEW_CONTINUOUS_SNAPPING
13228   /* prevent snapping with already pressed snap key when not allowed */
13229   if (player->is_snapping && !can_continue_snapping)
13230     return FALSE;
13231 #else
13232   if (player->is_snapping)
13233     return FALSE;
13234 #endif
13235
13236   player->MovDir = snap_direction;
13237
13238   if (player->MovPos == 0)
13239   {
13240     player->is_moving = FALSE;
13241     player->is_digging = FALSE;
13242     player->is_collecting = FALSE;
13243   }
13244
13245   player->is_dropping = FALSE;
13246   player->is_dropping_pressed = FALSE;
13247   player->drop_pressed_delay = 0;
13248
13249   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13250     return FALSE;
13251
13252   player->is_snapping = TRUE;
13253   player->is_active = TRUE;
13254
13255   if (player->MovPos == 0)
13256   {
13257     player->is_moving = FALSE;
13258     player->is_digging = FALSE;
13259     player->is_collecting = FALSE;
13260   }
13261
13262   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13263     DrawLevelField(player->last_jx, player->last_jy);
13264
13265   DrawLevelField(x, y);
13266
13267   return TRUE;
13268 }
13269
13270 boolean DropElement(struct PlayerInfo *player)
13271 {
13272   int old_element, new_element;
13273   int dropx = player->jx, dropy = player->jy;
13274   int drop_direction = player->MovDir;
13275   int drop_side = drop_direction;
13276   int drop_element = (player->inventory_size > 0 ?
13277                       player->inventory_element[player->inventory_size - 1] :
13278                       player->inventory_infinite_element != EL_UNDEFINED ?
13279                       player->inventory_infinite_element :
13280                       player->dynabombs_left > 0 ?
13281                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13282                       EL_UNDEFINED);
13283
13284   player->is_dropping_pressed = TRUE;
13285
13286   /* do not drop an element on top of another element; when holding drop key
13287      pressed without moving, dropped element must move away before the next
13288      element can be dropped (this is especially important if the next element
13289      is dynamite, which can be placed on background for historical reasons) */
13290   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13291     return MP_ACTION;
13292
13293   if (IS_THROWABLE(drop_element))
13294   {
13295     dropx += GET_DX_FROM_DIR(drop_direction);
13296     dropy += GET_DY_FROM_DIR(drop_direction);
13297
13298     if (!IN_LEV_FIELD(dropx, dropy))
13299       return FALSE;
13300   }
13301
13302   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13303   new_element = drop_element;           /* default: no change when dropping */
13304
13305   /* check if player is active, not moving and ready to drop */
13306   if (!player->active || player->MovPos || player->drop_delay > 0)
13307     return FALSE;
13308
13309   /* check if player has anything that can be dropped */
13310   if (new_element == EL_UNDEFINED)
13311     return FALSE;
13312
13313   /* check if drop key was pressed long enough for EM style dynamite */
13314   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13315     return FALSE;
13316
13317   /* check if anything can be dropped at the current position */
13318   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13319     return FALSE;
13320
13321   /* collected custom elements can only be dropped on empty fields */
13322   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13323     return FALSE;
13324
13325   if (old_element != EL_EMPTY)
13326     Back[dropx][dropy] = old_element;   /* store old element on this field */
13327
13328   ResetGfxAnimation(dropx, dropy);
13329   ResetRandomAnimationValue(dropx, dropy);
13330
13331   if (player->inventory_size > 0 ||
13332       player->inventory_infinite_element != EL_UNDEFINED)
13333   {
13334     if (player->inventory_size > 0)
13335     {
13336       player->inventory_size--;
13337
13338       DrawGameDoorValues();
13339
13340       if (new_element == EL_DYNAMITE)
13341         new_element = EL_DYNAMITE_ACTIVE;
13342       else if (new_element == EL_EM_DYNAMITE)
13343         new_element = EL_EM_DYNAMITE_ACTIVE;
13344       else if (new_element == EL_SP_DISK_RED)
13345         new_element = EL_SP_DISK_RED_ACTIVE;
13346     }
13347
13348     Feld[dropx][dropy] = new_element;
13349
13350     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13351       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13352                           el2img(Feld[dropx][dropy]), 0);
13353
13354     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13355
13356     /* needed if previous element just changed to "empty" in the last frame */
13357     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13358
13359     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13360                                player->index_bit, drop_side);
13361     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13362                                         CE_PLAYER_DROPS_X,
13363                                         player->index_bit, drop_side);
13364
13365     TestIfElementTouchesCustomElement(dropx, dropy);
13366   }
13367   else          /* player is dropping a dyna bomb */
13368   {
13369     player->dynabombs_left--;
13370
13371     Feld[dropx][dropy] = new_element;
13372
13373     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13374       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13375                           el2img(Feld[dropx][dropy]), 0);
13376
13377     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13378   }
13379
13380   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13381     InitField_WithBug1(dropx, dropy, FALSE);
13382
13383   new_element = Feld[dropx][dropy];     /* element might have changed */
13384
13385   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13386       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13387   {
13388     int move_direction, nextx, nexty;
13389
13390     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13391       MovDir[dropx][dropy] = drop_direction;
13392
13393     move_direction = MovDir[dropx][dropy];
13394     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13395     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13396
13397     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13398
13399 #if USE_FIX_IMPACT_COLLISION
13400     /* do not cause impact style collision by dropping elements that can fall */
13401     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13402 #else
13403     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13404 #endif
13405   }
13406
13407   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13408   player->is_dropping = TRUE;
13409
13410   player->drop_pressed_delay = 0;
13411   player->is_dropping_pressed = FALSE;
13412
13413   player->drop_x = dropx;
13414   player->drop_y = dropy;
13415
13416   return TRUE;
13417 }
13418
13419 /* ------------------------------------------------------------------------- */
13420 /* game sound playing functions                                              */
13421 /* ------------------------------------------------------------------------- */
13422
13423 static int *loop_sound_frame = NULL;
13424 static int *loop_sound_volume = NULL;
13425
13426 void InitPlayLevelSound()
13427 {
13428   int num_sounds = getSoundListSize();
13429
13430   checked_free(loop_sound_frame);
13431   checked_free(loop_sound_volume);
13432
13433   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13434   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13435 }
13436
13437 static void PlayLevelSound(int x, int y, int nr)
13438 {
13439   int sx = SCREENX(x), sy = SCREENY(y);
13440   int volume, stereo_position;
13441   int max_distance = 8;
13442   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13443
13444   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13445       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13446     return;
13447
13448   if (!IN_LEV_FIELD(x, y) ||
13449       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13450       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13451     return;
13452
13453   volume = SOUND_MAX_VOLUME;
13454
13455   if (!IN_SCR_FIELD(sx, sy))
13456   {
13457     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13458     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13459
13460     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13461   }
13462
13463   stereo_position = (SOUND_MAX_LEFT +
13464                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13465                      (SCR_FIELDX + 2 * max_distance));
13466
13467   if (IS_LOOP_SOUND(nr))
13468   {
13469     /* This assures that quieter loop sounds do not overwrite louder ones,
13470        while restarting sound volume comparison with each new game frame. */
13471
13472     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13473       return;
13474
13475     loop_sound_volume[nr] = volume;
13476     loop_sound_frame[nr] = FrameCounter;
13477   }
13478
13479   PlaySoundExt(nr, volume, stereo_position, type);
13480 }
13481
13482 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13483 {
13484   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13485                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13486                  y < LEVELY(BY1) ? LEVELY(BY1) :
13487                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13488                  sound_action);
13489 }
13490
13491 static void PlayLevelSoundAction(int x, int y, int action)
13492 {
13493   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13494 }
13495
13496 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13497 {
13498   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13499
13500   if (sound_effect != SND_UNDEFINED)
13501     PlayLevelSound(x, y, sound_effect);
13502 }
13503
13504 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13505                                               int action)
13506 {
13507   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13508
13509   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13510     PlayLevelSound(x, y, sound_effect);
13511 }
13512
13513 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13514 {
13515   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13516
13517   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13518     PlayLevelSound(x, y, sound_effect);
13519 }
13520
13521 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13522 {
13523   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13524
13525   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13526     StopSound(sound_effect);
13527 }
13528
13529 static void PlayLevelMusic()
13530 {
13531   if (levelset.music[level_nr] != MUS_UNDEFINED)
13532     PlayMusic(levelset.music[level_nr]);        /* from config file */
13533   else
13534     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13535 }
13536
13537 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13538 {
13539   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13540   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13541   int x = xx - 1 - offset;
13542   int y = yy - 1 - offset;
13543
13544   switch (sample)
13545   {
13546     case SAMPLE_blank:
13547       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13548       break;
13549
13550     case SAMPLE_roll:
13551       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13552       break;
13553
13554     case SAMPLE_stone:
13555       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13556       break;
13557
13558     case SAMPLE_nut:
13559       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13560       break;
13561
13562     case SAMPLE_crack:
13563       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13564       break;
13565
13566     case SAMPLE_bug:
13567       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13568       break;
13569
13570     case SAMPLE_tank:
13571       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13572       break;
13573
13574     case SAMPLE_android_clone:
13575       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13576       break;
13577
13578     case SAMPLE_android_move:
13579       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13580       break;
13581
13582     case SAMPLE_spring:
13583       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13584       break;
13585
13586     case SAMPLE_slurp:
13587       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13588       break;
13589
13590     case SAMPLE_eater:
13591       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13592       break;
13593
13594     case SAMPLE_eater_eat:
13595       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13596       break;
13597
13598     case SAMPLE_alien:
13599       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13600       break;
13601
13602     case SAMPLE_collect:
13603       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13604       break;
13605
13606     case SAMPLE_diamond:
13607       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13608       break;
13609
13610     case SAMPLE_squash:
13611       /* !!! CHECK THIS !!! */
13612 #if 1
13613       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13614 #else
13615       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13616 #endif
13617       break;
13618
13619     case SAMPLE_wonderfall:
13620       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13621       break;
13622
13623     case SAMPLE_drip:
13624       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13625       break;
13626
13627     case SAMPLE_push:
13628       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13629       break;
13630
13631     case SAMPLE_dirt:
13632       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13633       break;
13634
13635     case SAMPLE_acid:
13636       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13637       break;
13638
13639     case SAMPLE_ball:
13640       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13641       break;
13642
13643     case SAMPLE_grow:
13644       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13645       break;
13646
13647     case SAMPLE_wonder:
13648       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13649       break;
13650
13651     case SAMPLE_door:
13652       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13653       break;
13654
13655     case SAMPLE_exit_open:
13656       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13657       break;
13658
13659     case SAMPLE_exit_leave:
13660       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13661       break;
13662
13663     case SAMPLE_dynamite:
13664       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13665       break;
13666
13667     case SAMPLE_tick:
13668       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13669       break;
13670
13671     case SAMPLE_press:
13672       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13673       break;
13674
13675     case SAMPLE_wheel:
13676       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13677       break;
13678
13679     case SAMPLE_boom:
13680       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13681       break;
13682
13683     case SAMPLE_die:
13684       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13685       break;
13686
13687     case SAMPLE_time:
13688       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13689       break;
13690
13691     default:
13692       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13693       break;
13694   }
13695 }
13696
13697 #if 0
13698 void ChangeTime(int value)
13699 {
13700   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13701
13702   *time += value;
13703
13704   /* EMC game engine uses value from time counter of RND game engine */
13705   level.native_em_level->lev->time = *time;
13706
13707   DrawGameValue_Time(*time);
13708 }
13709
13710 void RaiseScore(int value)
13711 {
13712   /* EMC game engine and RND game engine have separate score counters */
13713   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13714                 &level.native_em_level->lev->score : &local_player->score);
13715
13716   *score += value;
13717
13718   DrawGameValue_Score(*score);
13719 }
13720 #endif
13721
13722 void RaiseScore(int value)
13723 {
13724   local_player->score += value;
13725
13726   DrawGameValue_Score(local_player->score);
13727 }
13728
13729 void RaiseScoreElement(int element)
13730 {
13731   switch (element)
13732   {
13733     case EL_EMERALD:
13734     case EL_BD_DIAMOND:
13735     case EL_EMERALD_YELLOW:
13736     case EL_EMERALD_RED:
13737     case EL_EMERALD_PURPLE:
13738     case EL_SP_INFOTRON:
13739       RaiseScore(level.score[SC_EMERALD]);
13740       break;
13741     case EL_DIAMOND:
13742       RaiseScore(level.score[SC_DIAMOND]);
13743       break;
13744     case EL_CRYSTAL:
13745       RaiseScore(level.score[SC_CRYSTAL]);
13746       break;
13747     case EL_PEARL:
13748       RaiseScore(level.score[SC_PEARL]);
13749       break;
13750     case EL_BUG:
13751     case EL_BD_BUTTERFLY:
13752     case EL_SP_ELECTRON:
13753       RaiseScore(level.score[SC_BUG]);
13754       break;
13755     case EL_SPACESHIP:
13756     case EL_BD_FIREFLY:
13757     case EL_SP_SNIKSNAK:
13758       RaiseScore(level.score[SC_SPACESHIP]);
13759       break;
13760     case EL_YAMYAM:
13761     case EL_DARK_YAMYAM:
13762       RaiseScore(level.score[SC_YAMYAM]);
13763       break;
13764     case EL_ROBOT:
13765       RaiseScore(level.score[SC_ROBOT]);
13766       break;
13767     case EL_PACMAN:
13768       RaiseScore(level.score[SC_PACMAN]);
13769       break;
13770     case EL_NUT:
13771       RaiseScore(level.score[SC_NUT]);
13772       break;
13773     case EL_DYNAMITE:
13774     case EL_EM_DYNAMITE:
13775     case EL_SP_DISK_RED:
13776     case EL_DYNABOMB_INCREASE_NUMBER:
13777     case EL_DYNABOMB_INCREASE_SIZE:
13778     case EL_DYNABOMB_INCREASE_POWER:
13779       RaiseScore(level.score[SC_DYNAMITE]);
13780       break;
13781     case EL_SHIELD_NORMAL:
13782     case EL_SHIELD_DEADLY:
13783       RaiseScore(level.score[SC_SHIELD]);
13784       break;
13785     case EL_EXTRA_TIME:
13786       RaiseScore(level.extra_time_score);
13787       break;
13788     case EL_KEY_1:
13789     case EL_KEY_2:
13790     case EL_KEY_3:
13791     case EL_KEY_4:
13792     case EL_EM_KEY_1:
13793     case EL_EM_KEY_2:
13794     case EL_EM_KEY_3:
13795     case EL_EM_KEY_4:
13796     case EL_EMC_KEY_5:
13797     case EL_EMC_KEY_6:
13798     case EL_EMC_KEY_7:
13799     case EL_EMC_KEY_8:
13800     case EL_DC_KEY_WHITE:
13801       RaiseScore(level.score[SC_KEY]);
13802       break;
13803     default:
13804       RaiseScore(element_info[element].collect_score);
13805       break;
13806   }
13807 }
13808
13809 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13810 {
13811   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13812   {
13813 #if defined(NETWORK_AVALIABLE)
13814     if (options.network)
13815       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13816     else
13817 #endif
13818     {
13819       if (quick_quit)
13820       {
13821         game_status = GAME_MODE_MAIN;
13822
13823         DrawMainMenu();
13824       }
13825       else
13826       {
13827         FadeOut(REDRAW_FIELD);
13828
13829         game_status = GAME_MODE_MAIN;
13830
13831         DrawAndFadeInMainMenu(REDRAW_FIELD);
13832       }
13833     }
13834   }
13835   else          /* continue playing the game */
13836   {
13837     if (tape.playing && tape.deactivate_display)
13838       TapeDeactivateDisplayOff(TRUE);
13839
13840     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13841
13842     if (tape.playing && tape.deactivate_display)
13843       TapeDeactivateDisplayOn();
13844   }
13845 }
13846
13847 void RequestQuitGame(boolean ask_if_really_quit)
13848 {
13849   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13850   boolean skip_request = AllPlayersGone || quick_quit;
13851
13852   RequestQuitGameExt(skip_request, quick_quit,
13853                      "Do you really want to quit the game ?");
13854 }
13855
13856
13857 /* ------------------------------------------------------------------------- */
13858 /* random generator functions                                                */
13859 /* ------------------------------------------------------------------------- */
13860
13861 unsigned int InitEngineRandom_RND(long seed)
13862 {
13863   game.num_random_calls = 0;
13864
13865 #if 0
13866   unsigned int rnd_seed = InitEngineRandom(seed);
13867
13868   printf("::: START RND: %d\n", rnd_seed);
13869
13870   return rnd_seed;
13871 #else
13872
13873   return InitEngineRandom(seed);
13874
13875 #endif
13876
13877 }
13878
13879 unsigned int RND(int max)
13880 {
13881   if (max > 0)
13882   {
13883     game.num_random_calls++;
13884
13885     return GetEngineRandom(max);
13886   }
13887
13888   return 0;
13889 }
13890
13891
13892 /* ------------------------------------------------------------------------- */
13893 /* game engine snapshot handling functions                                   */
13894 /* ------------------------------------------------------------------------- */
13895
13896 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13897
13898 struct EngineSnapshotInfo
13899 {
13900   /* runtime values for custom element collect score */
13901   int collect_score[NUM_CUSTOM_ELEMENTS];
13902
13903   /* runtime values for group element choice position */
13904   int choice_pos[NUM_GROUP_ELEMENTS];
13905
13906   /* runtime values for belt position animations */
13907   int belt_graphic[4 * NUM_BELT_PARTS];
13908   int belt_anim_mode[4 * NUM_BELT_PARTS];
13909 };
13910
13911 struct EngineSnapshotNodeInfo
13912 {
13913   void *buffer_orig;
13914   void *buffer_copy;
13915   int size;
13916 };
13917
13918 static struct EngineSnapshotInfo engine_snapshot_rnd;
13919 static ListNode *engine_snapshot_list = NULL;
13920 static char *snapshot_level_identifier = NULL;
13921 static int snapshot_level_nr = -1;
13922
13923 void FreeEngineSnapshot()
13924 {
13925   while (engine_snapshot_list != NULL)
13926     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13927                        checked_free);
13928
13929   setString(&snapshot_level_identifier, NULL);
13930   snapshot_level_nr = -1;
13931 }
13932
13933 static void SaveEngineSnapshotValues_RND()
13934 {
13935   static int belt_base_active_element[4] =
13936   {
13937     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13938     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13939     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13940     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13941   };
13942   int i, j;
13943
13944   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13945   {
13946     int element = EL_CUSTOM_START + i;
13947
13948     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13949   }
13950
13951   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13952   {
13953     int element = EL_GROUP_START + i;
13954
13955     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13956   }
13957
13958   for (i = 0; i < 4; i++)
13959   {
13960     for (j = 0; j < NUM_BELT_PARTS; j++)
13961     {
13962       int element = belt_base_active_element[i] + j;
13963       int graphic = el2img(element);
13964       int anim_mode = graphic_info[graphic].anim_mode;
13965
13966       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13967       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13968     }
13969   }
13970 }
13971
13972 static void LoadEngineSnapshotValues_RND()
13973 {
13974   unsigned long num_random_calls = game.num_random_calls;
13975   int i, j;
13976
13977   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13978   {
13979     int element = EL_CUSTOM_START + i;
13980
13981     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13982   }
13983
13984   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13985   {
13986     int element = EL_GROUP_START + i;
13987
13988     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13989   }
13990
13991   for (i = 0; i < 4; i++)
13992   {
13993     for (j = 0; j < NUM_BELT_PARTS; j++)
13994     {
13995       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13996       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13997
13998       graphic_info[graphic].anim_mode = anim_mode;
13999     }
14000   }
14001
14002   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14003   {
14004     InitRND(tape.random_seed);
14005     for (i = 0; i < num_random_calls; i++)
14006       RND(1);
14007   }
14008
14009   if (game.num_random_calls != num_random_calls)
14010   {
14011     Error(ERR_INFO, "number of random calls out of sync");
14012     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14013     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14014     Error(ERR_EXIT, "this should not happen -- please debug");
14015   }
14016 }
14017
14018 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14019 {
14020   struct EngineSnapshotNodeInfo *bi =
14021     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14022
14023   bi->buffer_orig = buffer;
14024   bi->buffer_copy = checked_malloc(size);
14025   bi->size = size;
14026
14027   memcpy(bi->buffer_copy, buffer, size);
14028
14029   addNodeToList(&engine_snapshot_list, NULL, bi);
14030 }
14031
14032 void SaveEngineSnapshot()
14033 {
14034   FreeEngineSnapshot();         /* free previous snapshot, if needed */
14035
14036   if (level_editor_test_game)   /* do not save snapshots from editor */
14037     return;
14038
14039   /* copy some special values to a structure better suited for the snapshot */
14040
14041   SaveEngineSnapshotValues_RND();
14042   SaveEngineSnapshotValues_EM();
14043
14044   /* save values stored in special snapshot structure */
14045
14046   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14047   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14048
14049   /* save further RND engine values */
14050
14051   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
14052   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
14053   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
14054
14055   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
14056   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
14057   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
14058   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
14059
14060   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14061   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14062   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14063   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14064   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14065
14066   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14067   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14068   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14069
14070   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14071
14072   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14073
14074   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14075   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14076
14077   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
14078   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
14079   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
14080   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14081   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14082   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14083   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14084   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
14085   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
14086   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14087   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
14088   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14089   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14090   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14091   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14092   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14093   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
14094   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
14095
14096   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14097   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14098
14099   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14100   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14101   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14102
14103   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14104   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14105
14106   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14107   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14108   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14109   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14110   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14111
14112   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14113   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14114
14115   /* save level identification information */
14116
14117   setString(&snapshot_level_identifier, leveldir_current->identifier);
14118   snapshot_level_nr = level_nr;
14119
14120 #if 0
14121   ListNode *node = engine_snapshot_list;
14122   int num_bytes = 0;
14123
14124   while (node != NULL)
14125   {
14126     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14127
14128     node = node->next;
14129   }
14130
14131   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14132 #endif
14133 }
14134
14135 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
14136 {
14137   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
14138 }
14139
14140 void LoadEngineSnapshot()
14141 {
14142   ListNode *node = engine_snapshot_list;
14143
14144   if (engine_snapshot_list == NULL)
14145     return;
14146
14147   while (node != NULL)
14148   {
14149     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
14150
14151     node = node->next;
14152   }
14153
14154   /* restore special values from snapshot structure */
14155
14156   LoadEngineSnapshotValues_RND();
14157   LoadEngineSnapshotValues_EM();
14158 }
14159
14160 boolean CheckEngineSnapshot()
14161 {
14162   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14163           snapshot_level_nr == level_nr);
14164 }
14165
14166
14167 /* ---------- new game button stuff ---------------------------------------- */
14168
14169 /* graphic position values for game buttons */
14170 #define GAME_BUTTON_XSIZE       30
14171 #define GAME_BUTTON_YSIZE       30
14172 #define GAME_BUTTON_XPOS        5
14173 #define GAME_BUTTON_YPOS        215
14174 #define SOUND_BUTTON_XPOS       5
14175 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
14176
14177 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14178 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14179 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14180 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
14181 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
14182 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
14183
14184 static struct
14185 {
14186   int x, y;
14187   int gadget_id;
14188   char *infotext;
14189 } gamebutton_info[NUM_GAME_BUTTONS] =
14190 {
14191   {
14192     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
14193     GAME_CTRL_ID_STOP,
14194     "stop game"
14195   },
14196   {
14197     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
14198     GAME_CTRL_ID_PAUSE,
14199     "pause game"
14200   },
14201   {
14202     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
14203     GAME_CTRL_ID_PLAY,
14204     "play game"
14205   },
14206   {
14207     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
14208     SOUND_CTRL_ID_MUSIC,
14209     "background music on/off"
14210   },
14211   {
14212     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
14213     SOUND_CTRL_ID_LOOPS,
14214     "sound loops on/off"
14215   },
14216   {
14217     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
14218     SOUND_CTRL_ID_SIMPLE,
14219     "normal sounds on/off"
14220   }
14221 };
14222
14223 void CreateGameButtons()
14224 {
14225   int i;
14226
14227   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14228   {
14229     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
14230     struct GadgetInfo *gi;
14231     int button_type;
14232     boolean checked;
14233     unsigned long event_mask;
14234     int gd_xoffset, gd_yoffset;
14235     int gd_x1, gd_x2, gd_y1, gd_y2;
14236     int id = i;
14237
14238     gd_xoffset = gamebutton_info[i].x;
14239     gd_yoffset = gamebutton_info[i].y;
14240     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
14241     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
14242
14243     if (id == GAME_CTRL_ID_STOP ||
14244         id == GAME_CTRL_ID_PAUSE ||
14245         id == GAME_CTRL_ID_PLAY)
14246     {
14247       button_type = GD_TYPE_NORMAL_BUTTON;
14248       checked = FALSE;
14249       event_mask = GD_EVENT_RELEASED;
14250       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14251       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14252     }
14253     else
14254     {
14255       button_type = GD_TYPE_CHECK_BUTTON;
14256       checked =
14257         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14258          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14259          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14260       event_mask = GD_EVENT_PRESSED;
14261       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14262       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14263     }
14264
14265     gi = CreateGadget(GDI_CUSTOM_ID, id,
14266                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14267                       GDI_X, DX + gd_xoffset,
14268                       GDI_Y, DY + gd_yoffset,
14269                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14270                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14271                       GDI_TYPE, button_type,
14272                       GDI_STATE, GD_BUTTON_UNPRESSED,
14273                       GDI_CHECKED, checked,
14274                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14275                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14276                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14277                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14278                       GDI_EVENT_MASK, event_mask,
14279                       GDI_CALLBACK_ACTION, HandleGameButtons,
14280                       GDI_END);
14281
14282     if (gi == NULL)
14283       Error(ERR_EXIT, "cannot create gadget");
14284
14285     game_gadget[id] = gi;
14286   }
14287 }
14288
14289 void FreeGameButtons()
14290 {
14291   int i;
14292
14293   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14294     FreeGadget(game_gadget[i]);
14295 }
14296
14297 static void MapGameButtons()
14298 {
14299   int i;
14300
14301   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14302     MapGadget(game_gadget[i]);
14303 }
14304
14305 void UnmapGameButtons()
14306 {
14307   int i;
14308
14309   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14310     UnmapGadget(game_gadget[i]);
14311 }
14312
14313 static void HandleGameButtons(struct GadgetInfo *gi)
14314 {
14315   int id = gi->custom_id;
14316
14317   if (game_status != GAME_MODE_PLAYING)
14318     return;
14319
14320   switch (id)
14321   {
14322     case GAME_CTRL_ID_STOP:
14323       if (tape.playing)
14324         TapeStop();
14325       else
14326         RequestQuitGame(TRUE);
14327       break;
14328
14329     case GAME_CTRL_ID_PAUSE:
14330       if (options.network)
14331       {
14332 #if defined(NETWORK_AVALIABLE)
14333         if (tape.pausing)
14334           SendToServer_ContinuePlaying();
14335         else
14336           SendToServer_PausePlaying();
14337 #endif
14338       }
14339       else
14340         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14341       break;
14342
14343     case GAME_CTRL_ID_PLAY:
14344       if (tape.pausing)
14345       {
14346 #if defined(NETWORK_AVALIABLE)
14347         if (options.network)
14348           SendToServer_ContinuePlaying();
14349         else
14350 #endif
14351         {
14352           tape.pausing = FALSE;
14353           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14354         }
14355       }
14356       break;
14357
14358     case SOUND_CTRL_ID_MUSIC:
14359       if (setup.sound_music)
14360       { 
14361         setup.sound_music = FALSE;
14362         FadeMusic();
14363       }
14364       else if (audio.music_available)
14365       { 
14366         setup.sound = setup.sound_music = TRUE;
14367
14368         SetAudioMode(setup.sound);
14369
14370         PlayLevelMusic();
14371       }
14372       break;
14373
14374     case SOUND_CTRL_ID_LOOPS:
14375       if (setup.sound_loops)
14376         setup.sound_loops = FALSE;
14377       else if (audio.loops_available)
14378       {
14379         setup.sound = setup.sound_loops = TRUE;
14380         SetAudioMode(setup.sound);
14381       }
14382       break;
14383
14384     case SOUND_CTRL_ID_SIMPLE:
14385       if (setup.sound_simple)
14386         setup.sound_simple = FALSE;
14387       else if (audio.sound_available)
14388       {
14389         setup.sound = setup.sound_simple = TRUE;
14390         SetAudioMode(setup.sound);
14391       }
14392       break;
14393
14394     default:
14395       break;
14396   }
14397 }