77df179d6664144f52f88ecdaf4de9710dab5d46
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 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 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME                 29
63 #define YY_TIME                 194
64
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL                (DX + XX_LEVEL)
67 #define DY_LEVEL                (DY + YY_LEVEL)
68 #define DX_EMERALDS             (DX + XX_EMERALDS)
69 #define DY_EMERALDS             (DY + YY_EMERALDS)
70 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
72 #define DX_KEYS                 (DX + XX_KEYS)
73 #define DY_KEYS                 (DY + YY_KEYS)
74 #define DX_SCORE                (DX + XX_SCORE)
75 #define DY_SCORE                (DY + YY_SCORE)
76 #define DX_TIME                 (DX + XX_TIME)
77 #define DY_TIME                 (DY + YY_TIME)
78
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF  -1
81 #define INITIAL_MOVE_DELAY_ON   0
82
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED   4
86
87 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
91
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
94
95 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
96
97 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
98                                  RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
100                                  RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                     (element_info[e].move_delay_random))
103
104 #if 1
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
106                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
107                                         (condition) ||                  \
108                                         (DONT_COLLIDE_WITH(e) &&        \
109                                          IS_PLAYER(x, y) &&             \
110                                          !PLAYER_ENEMY_PROTECTED(x, y))))
111 #else
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
113                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
114                                         (condition) ||                  \
115                                         (DONT_COLLIDE_WITH(e) &&        \
116                                          IS_FREE_OR_PLAYER(x, y))))
117 #endif
118
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
120                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
121                                         (condition)))
122
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
124         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
125
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
127         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
128
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
130         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
131
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
133
134 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
135                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
136                                         Feld[x][y] == EL_DIAMOND))
137
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
139                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
140                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
141
142 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
143                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
144                                         IS_AMOEBOID(Feld[x][y])))
145
146 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
147                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
148                                         IS_FOOD_PIG(Feld[x][y])))
149
150 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
151                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
152                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
153                                         Feld[x][y] == EL_EXIT_OPEN ||   \
154                                         Feld[x][y] == EL_ACID))
155
156 #if 0
157 #if 1
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y)                               \
159                 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
160 #else
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y)                               \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
163                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
164 #endif
165 #endif
166
167 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
168 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
169 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
170 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
171
172 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
173         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
174
175 #if 1
176 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
177                 (!IS_PLAYER(x, y) &&                                    \
178                  (Feld[x][y] == EL_ACID ||                              \
179                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
180 #else
181 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
182                 (!IS_PLAYER(x, y) &&                                    \
183                  (Feld[x][y] == EL_ACID ||                              \
184                   Feld[x][y] == MOVE_ENTER_EL(e) ||                     \
185                   (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) &&                \
186                    IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
187 #endif
188
189 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
190         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
191
192 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
193                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
194
195 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
196 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
197
198 /* game button identifiers */
199 #define GAME_CTRL_ID_STOP               0
200 #define GAME_CTRL_ID_PAUSE              1
201 #define GAME_CTRL_ID_PLAY               2
202 #define SOUND_CTRL_ID_MUSIC             3
203 #define SOUND_CTRL_ID_LOOPS             4
204 #define SOUND_CTRL_ID_SIMPLE            5
205
206 #define NUM_GAME_BUTTONS                6
207
208
209 /* forward declaration for internal use */
210
211 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
212 static boolean MovePlayer(struct PlayerInfo *, int, int);
213 static void ScrollPlayer(struct PlayerInfo *, int);
214 static void ScrollScreen(struct PlayerInfo *, int);
215
216 static void InitBeltMovement(void);
217 static void CloseAllOpenTimegates(void);
218 static void CheckGravityMovement(struct PlayerInfo *);
219 static void KillHeroUnlessEnemyProtected(int, int);
220 static void KillHeroUnlessExplosionProtected(int, int);
221
222 static void TestIfPlayerTouchesCustomElement(int, int);
223 static void TestIfElementTouchesCustomElement(int, int);
224 static void TestIfElementHitsCustomElement(int, int, int);
225
226 static void ChangeElement(int, int, int);
227 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
228 static boolean CheckTriggeredElementChange(int, int, int, int);
229 static boolean CheckElementSideChange(int, int, int, int, int, int);
230 static boolean CheckElementChange(int, int, int, int);
231
232 static void PlayLevelSound(int, int, int);
233 static void PlayLevelSoundNearest(int, int, int);
234 static void PlayLevelSoundAction(int, int, int);
235 static void PlayLevelSoundElementAction(int, int, int, int);
236 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
237 static void PlayLevelSoundActionIfLoop(int, int, int);
238 static void StopLevelSoundActionIfLoop(int, int, int);
239 static void PlayLevelMusic();
240
241 static void MapGameButtons();
242 static void HandleGameButtons(struct GadgetInfo *);
243
244 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
245
246
247 /* ------------------------------------------------------------------------- */
248 /* definition of elements that automatically change to other elements after  */
249 /* a specified time, eventually calling a function when changing             */
250 /* ------------------------------------------------------------------------- */
251
252 /* forward declaration for changer functions */
253 static void InitBuggyBase(int x, int y);
254 static void WarnBuggyBase(int x, int y);
255
256 static void InitTrap(int x, int y);
257 static void ActivateTrap(int x, int y);
258 static void ChangeActiveTrap(int x, int y);
259
260 static void InitRobotWheel(int x, int y);
261 static void RunRobotWheel(int x, int y);
262 static void StopRobotWheel(int x, int y);
263
264 static void InitTimegateWheel(int x, int y);
265 static void RunTimegateWheel(int x, int y);
266
267 struct ChangingElementInfo
268 {
269   int element;
270   int target_element;
271   int change_delay;
272   void (*pre_change_function)(int x, int y);
273   void (*change_function)(int x, int y);
274   void (*post_change_function)(int x, int y);
275 };
276
277 static struct ChangingElementInfo change_delay_list[] =
278 {
279   {
280     EL_NUT_BREAKING,
281     EL_EMERALD,
282     6,
283     NULL,
284     NULL,
285     NULL
286   },
287   {
288     EL_PEARL_BREAKING,
289     EL_EMPTY,
290     8,
291     NULL,
292     NULL,
293     NULL
294   },
295   {
296     EL_EXIT_OPENING,
297     EL_EXIT_OPEN,
298     29,
299     NULL,
300     NULL,
301     NULL
302   },
303   {
304     EL_EXIT_CLOSING,
305     EL_EXIT_CLOSED,
306     29,
307     NULL,
308     NULL,
309     NULL
310   },
311   {
312     EL_SP_EXIT_OPENING,
313     EL_SP_EXIT_OPEN,
314     29,
315     NULL,
316     NULL,
317     NULL
318   },
319   {
320     EL_SP_EXIT_CLOSING,
321     EL_SP_EXIT_CLOSED,
322     29,
323     NULL,
324     NULL,
325     NULL
326   },
327   {
328     EL_SWITCHGATE_OPENING,
329     EL_SWITCHGATE_OPEN,
330     29,
331     NULL,
332     NULL,
333     NULL
334   },
335   {
336     EL_SWITCHGATE_CLOSING,
337     EL_SWITCHGATE_CLOSED,
338     29,
339     NULL,
340     NULL,
341     NULL
342   },
343   {
344     EL_TIMEGATE_OPENING,
345     EL_TIMEGATE_OPEN,
346     29,
347     NULL,
348     NULL,
349     NULL
350   },
351   {
352     EL_TIMEGATE_CLOSING,
353     EL_TIMEGATE_CLOSED,
354     29,
355     NULL,
356     NULL,
357     NULL
358   },
359
360   {
361     EL_ACID_SPLASH_LEFT,
362     EL_EMPTY,
363     8,
364     NULL,
365     NULL,
366     NULL
367   },
368   {
369     EL_ACID_SPLASH_RIGHT,
370     EL_EMPTY,
371     8,
372     NULL,
373     NULL,
374     NULL
375   },
376   {
377     EL_SP_BUGGY_BASE,
378     EL_SP_BUGGY_BASE_ACTIVATING,
379     0,
380     InitBuggyBase,
381     NULL,
382     NULL
383   },
384   {
385     EL_SP_BUGGY_BASE_ACTIVATING,
386     EL_SP_BUGGY_BASE_ACTIVE,
387     0,
388     InitBuggyBase,
389     NULL,
390     NULL
391   },
392   {
393     EL_SP_BUGGY_BASE_ACTIVE,
394     EL_SP_BUGGY_BASE,
395     0,
396     InitBuggyBase,
397     WarnBuggyBase,
398     NULL
399   },
400   {
401     EL_TRAP,
402     EL_TRAP_ACTIVE,
403     0,
404     InitTrap,
405     NULL,
406     ActivateTrap
407   },
408   {
409     EL_TRAP_ACTIVE,
410     EL_TRAP,
411     31,
412     NULL,
413     ChangeActiveTrap,
414     NULL
415   },
416   {
417     EL_ROBOT_WHEEL_ACTIVE,
418     EL_ROBOT_WHEEL,
419     0,
420     InitRobotWheel,
421     RunRobotWheel,
422     StopRobotWheel
423   },
424   {
425     EL_TIMEGATE_SWITCH_ACTIVE,
426     EL_TIMEGATE_SWITCH,
427     0,
428     InitTimegateWheel,
429     RunTimegateWheel,
430     NULL
431   },
432
433   {
434     EL_UNDEFINED,
435     EL_UNDEFINED,
436     -1,
437     NULL,
438     NULL,
439     NULL
440   }
441 };
442
443 struct
444 {
445   int element;
446   int push_delay_fixed, push_delay_random;
447 }
448 push_delay_list[] =
449 {
450   { EL_SPRING,                  0, 0 },
451   { EL_BALLOON,                 0, 0 },
452
453   { EL_SOKOBAN_OBJECT,          2, 0 },
454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
455   { EL_SATELLITE,               2, 0 },
456   { EL_SP_DISK_YELLOW,          2, 0 },
457
458   { EL_UNDEFINED,               0, 0 },
459 };
460
461 struct
462 {
463   int element;
464   int move_stepsize;
465 }
466 move_stepsize_list[] =
467 {
468   { EL_AMOEBA_DROP,             2 },
469   { EL_AMOEBA_DROPPING,         2 },
470   { EL_QUICKSAND_FILLING,       1 },
471   { EL_QUICKSAND_EMPTYING,      1 },
472   { EL_MAGIC_WALL_FILLING,      2 },
473   { EL_BD_MAGIC_WALL_FILLING,   2 },
474   { EL_MAGIC_WALL_EMPTYING,     2 },
475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
476
477   { EL_UNDEFINED,               0 },
478 };
479
480 struct
481 {
482   int element;
483   int count;
484 }
485 collect_count_list[] =
486 {
487   { EL_EMERALD,                 1 },
488   { EL_BD_DIAMOND,              1 },
489   { EL_EMERALD_YELLOW,          1 },
490   { EL_EMERALD_RED,             1 },
491   { EL_EMERALD_PURPLE,          1 },
492   { EL_DIAMOND,                 3 },
493   { EL_SP_INFOTRON,             1 },
494   { EL_PEARL,                   5 },
495   { EL_CRYSTAL,                 8 },
496
497   { EL_UNDEFINED,               0 },
498 };
499
500 struct
501 {
502   int element;
503   int direction;
504 }
505 tube_access[] =
506 {
507   { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
508   { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
509   { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
510   { EL_TUBE_VERTICAL_LEFT,      MV_LEFT |            MV_UP | MV_DOWN },
511   { EL_TUBE_VERTICAL_RIGHT,               MV_RIGHT | MV_UP | MV_DOWN },
512   { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT | MV_UP           },
513   { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT |         MV_DOWN },
514   { EL_TUBE_LEFT_UP,            MV_LEFT |            MV_UP           },
515   { EL_TUBE_LEFT_DOWN,          MV_LEFT |                    MV_DOWN },
516   { EL_TUBE_RIGHT_UP,                     MV_RIGHT | MV_UP           },
517   { EL_TUBE_RIGHT_DOWN,                   MV_RIGHT |         MV_DOWN },
518
519   { EL_UNDEFINED,               0                                    }
520 };
521
522 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
523
524 #define IS_AUTO_CHANGING(e)     (element_info[e].change_events & \
525                                  CH_EVENT_BIT(CE_DELAY))
526 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
527 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
528                                  IS_JUST_CHANGING(x, y))
529
530 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
531
532
533 void GetPlayerConfig()
534 {
535   if (!audio.sound_available)
536     setup.sound_simple = FALSE;
537
538   if (!audio.loops_available)
539     setup.sound_loops = FALSE;
540
541   if (!audio.music_available)
542     setup.sound_music = FALSE;
543
544   if (!video.fullscreen_available)
545     setup.fullscreen = FALSE;
546
547   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
548
549   SetAudioMode(setup.sound);
550   InitJoysticks();
551 }
552
553 static int getBeltNrFromBeltElement(int element)
554 {
555   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
556           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
557           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
558 }
559
560 static int getBeltNrFromBeltActiveElement(int element)
561 {
562   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
563           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
564           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
565 }
566
567 static int getBeltNrFromBeltSwitchElement(int element)
568 {
569   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
570           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
571           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
572 }
573
574 static int getBeltDirNrFromBeltSwitchElement(int element)
575 {
576   static int belt_base_element[4] =
577   {
578     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
579     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
580     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
581     EL_CONVEYOR_BELT_4_SWITCH_LEFT
582   };
583
584   int belt_nr = getBeltNrFromBeltSwitchElement(element);
585   int belt_dir_nr = element - belt_base_element[belt_nr];
586
587   return (belt_dir_nr % 3);
588 }
589
590 static int getBeltDirFromBeltSwitchElement(int element)
591 {
592   static int belt_move_dir[3] =
593   {
594     MV_LEFT,
595     MV_NO_MOVING,
596     MV_RIGHT
597   };
598
599   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
600
601   return belt_move_dir[belt_dir_nr];
602 }
603
604 static void InitPlayerField(int x, int y, int element, boolean init_game)
605 {
606   if (element == EL_SP_MURPHY)
607   {
608     if (init_game)
609     {
610       if (stored_player[0].present)
611       {
612         Feld[x][y] = EL_SP_MURPHY_CLONE;
613
614         return;
615       }
616       else
617       {
618         stored_player[0].use_murphy_graphic = TRUE;
619       }
620
621       Feld[x][y] = EL_PLAYER_1;
622     }
623   }
624
625   if (init_game)
626   {
627     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
628     int jx = player->jx, jy = player->jy;
629
630     player->present = TRUE;
631
632     player->block_last_field = (element == EL_SP_MURPHY ?
633                                 level.sp_block_last_field :
634                                 level.block_last_field);
635
636     if (!options.network || player->connected)
637     {
638       player->active = TRUE;
639
640       /* remove potentially duplicate players */
641       if (StorePlayer[jx][jy] == Feld[x][y])
642         StorePlayer[jx][jy] = 0;
643
644       StorePlayer[x][y] = Feld[x][y];
645
646       if (options.debug)
647       {
648         printf("Player %d activated.\n", player->element_nr);
649         printf("[Local player is %d and currently %s.]\n",
650                local_player->element_nr,
651                local_player->active ? "active" : "not active");
652       }
653     }
654
655     Feld[x][y] = EL_EMPTY;
656     player->jx = player->last_jx = x;
657     player->jy = player->last_jy = y;
658   }
659 }
660
661 static void InitField(int x, int y, boolean init_game)
662 {
663   int element = Feld[x][y];
664
665   switch (element)
666   {
667     case EL_SP_MURPHY:
668     case EL_PLAYER_1:
669     case EL_PLAYER_2:
670     case EL_PLAYER_3:
671     case EL_PLAYER_4:
672       InitPlayerField(x, y, element, init_game);
673       break;
674
675     case EL_SOKOBAN_FIELD_PLAYER:
676       element = Feld[x][y] = EL_PLAYER_1;
677       InitField(x, y, init_game);
678
679       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
680       InitField(x, y, init_game);
681       break;
682
683     case EL_SOKOBAN_FIELD_EMPTY:
684       local_player->sokobanfields_still_needed++;
685       break;
686
687     case EL_STONEBLOCK:
688       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
689         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
690       else if (x > 0 && Feld[x-1][y] == EL_ACID)
691         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
692       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
693         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
694       else if (y > 0 && Feld[x][y-1] == EL_ACID)
695         Feld[x][y] = EL_ACID_POOL_BOTTOM;
696       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
697         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
698       break;
699
700     case EL_BUG_RIGHT:
701     case EL_BUG_UP:
702     case EL_BUG_LEFT:
703     case EL_BUG_DOWN:
704     case EL_BUG:
705     case EL_SPACESHIP_RIGHT:
706     case EL_SPACESHIP_UP:
707     case EL_SPACESHIP_LEFT:
708     case EL_SPACESHIP_DOWN:
709     case EL_SPACESHIP:
710     case EL_BD_BUTTERFLY_RIGHT:
711     case EL_BD_BUTTERFLY_UP:
712     case EL_BD_BUTTERFLY_LEFT:
713     case EL_BD_BUTTERFLY_DOWN:
714     case EL_BD_BUTTERFLY:
715     case EL_BD_FIREFLY_RIGHT:
716     case EL_BD_FIREFLY_UP:
717     case EL_BD_FIREFLY_LEFT:
718     case EL_BD_FIREFLY_DOWN:
719     case EL_BD_FIREFLY:
720     case EL_PACMAN_RIGHT:
721     case EL_PACMAN_UP:
722     case EL_PACMAN_LEFT:
723     case EL_PACMAN_DOWN:
724     case EL_YAMYAM:
725     case EL_DARK_YAMYAM:
726     case EL_ROBOT:
727     case EL_PACMAN:
728     case EL_SP_SNIKSNAK:
729     case EL_SP_ELECTRON:
730     case EL_MOLE_LEFT:
731     case EL_MOLE_RIGHT:
732     case EL_MOLE_UP:
733     case EL_MOLE_DOWN:
734     case EL_MOLE:
735       InitMovDir(x, y);
736       break;
737
738     case EL_AMOEBA_FULL:
739     case EL_BD_AMOEBA:
740       InitAmoebaNr(x, y);
741       break;
742
743     case EL_AMOEBA_DROP:
744       if (y == lev_fieldy - 1)
745       {
746         Feld[x][y] = EL_AMOEBA_GROWING;
747         Store[x][y] = EL_AMOEBA_WET;
748       }
749       break;
750
751     case EL_DYNAMITE_ACTIVE:
752     case EL_SP_DISK_RED_ACTIVE:
753     case EL_DYNABOMB_PLAYER_1_ACTIVE:
754     case EL_DYNABOMB_PLAYER_2_ACTIVE:
755     case EL_DYNABOMB_PLAYER_3_ACTIVE:
756     case EL_DYNABOMB_PLAYER_4_ACTIVE:
757       MovDelay[x][y] = 96;
758       break;
759
760     case EL_LAMP:
761       local_player->lights_still_needed++;
762       break;
763
764     case EL_PENGUIN:
765       local_player->friends_still_needed++;
766       break;
767
768     case EL_PIG:
769     case EL_DRAGON:
770       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
771       break;
772
773 #if 0
774     case EL_SP_EMPTY:
775       Feld[x][y] = EL_EMPTY;
776       break;
777 #endif
778
779 #if 0
780     case EL_EM_KEY_1_FILE:
781       Feld[x][y] = EL_EM_KEY_1;
782       break;
783     case EL_EM_KEY_2_FILE:
784       Feld[x][y] = EL_EM_KEY_2;
785       break;
786     case EL_EM_KEY_3_FILE:
787       Feld[x][y] = EL_EM_KEY_3;
788       break;
789     case EL_EM_KEY_4_FILE:
790       Feld[x][y] = EL_EM_KEY_4;
791       break;
792 #endif
793
794     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
795     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
796     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
797     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
798     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
799     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
800     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
801     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
802     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
803     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
804     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
805     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
806       if (init_game)
807       {
808         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
809         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
810         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
811
812         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
813         {
814           game.belt_dir[belt_nr] = belt_dir;
815           game.belt_dir_nr[belt_nr] = belt_dir_nr;
816         }
817         else    /* more than one switch -- set it like the first switch */
818         {
819           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
820         }
821       }
822       break;
823
824     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
825       if (init_game)
826         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
827       break;
828
829     case EL_LIGHT_SWITCH_ACTIVE:
830       if (init_game)
831         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
832       break;
833
834     default:
835       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
836         InitMovDir(x, y);
837       else if (IS_GROUP_ELEMENT(element))
838       {
839         struct ElementGroupInfo *group = element_info[element].group;
840         int last_anim_random_frame = gfx.anim_random_frame;
841         int element_pos;
842
843         if (group->choice_mode == ANIM_RANDOM)
844           gfx.anim_random_frame = RND(group->num_elements_resolved);
845
846         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
847                                         group->choice_mode, 0,
848                                         group->choice_pos);
849
850         if (group->choice_mode == ANIM_RANDOM)
851           gfx.anim_random_frame = last_anim_random_frame;
852
853         group->choice_pos++;
854
855         Feld[x][y] = group->element_resolved[element_pos];
856
857         InitField(x, y, init_game);
858       }
859       break;
860   }
861 }
862
863 void DrawGameDoorValues()
864 {
865   int i, j;
866
867   for (i = 0; i < MAX_PLAYERS; i++)
868     for (j = 0; j < 4; j++)
869       if (stored_player[i].key[j])
870         DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
871                            el2edimg(EL_KEY_1 + j));
872
873   DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
874            int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
875   DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
876            int2str(local_player->inventory_size, 3), FONT_TEXT_2);
877   DrawText(DX + XX_SCORE, DY + YY_SCORE,
878            int2str(local_player->score, 5), FONT_TEXT_2);
879   DrawText(DX + XX_TIME, DY + YY_TIME,
880            int2str(TimeLeft, 3), FONT_TEXT_2);
881 }
882
883 static void resolve_group_element(int group_element, int recursion_depth)
884 {
885   static int group_nr;
886   static struct ElementGroupInfo *group;
887   struct ElementGroupInfo *actual_group = element_info[group_element].group;
888   int i;
889
890   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
891   {
892     Error(ERR_WARN, "recursion too deep when resolving group element %d",
893           group_element - EL_GROUP_START + 1);
894
895     /* replace element which caused too deep recursion by question mark */
896     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
897
898     return;
899   }
900
901   if (recursion_depth == 0)                     /* initialization */
902   {
903     group = element_info[group_element].group;
904     group_nr = group_element - EL_GROUP_START;
905
906     group->num_elements_resolved = 0;
907     group->choice_pos = 0;
908   }
909
910   for (i = 0; i < actual_group->num_elements; i++)
911   {
912     int element = actual_group->element[i];
913
914     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
915       break;
916
917     if (IS_GROUP_ELEMENT(element))
918       resolve_group_element(element, recursion_depth + 1);
919     else
920     {
921       group->element_resolved[group->num_elements_resolved++] = element;
922       element_info[element].in_group[group_nr] = TRUE;
923     }
924   }
925
926 #if 0
927   if (recursion_depth == 0 && group_element <= EL_GROUP_4)
928   {
929     printf("::: group %d: %d resolved elements\n",
930            group_element - EL_GROUP_START, group->num_elements_resolved);
931     for (i = 0; i < group->num_elements_resolved; i++)
932       printf("::: - %d ['%s']\n", group->element_resolved[i],
933              element_info[group->element_resolved[i]].token_name);
934   }
935 #endif
936 }
937
938
939 /*
940   =============================================================================
941   InitGameEngine()
942   -----------------------------------------------------------------------------
943   initialize game engine due to level / tape version number
944   =============================================================================
945 */
946
947 static void InitGameEngine()
948 {
949   int i, j, k;
950
951   /* set game engine from tape file when re-playing, else from level file */
952   game.engine_version = (tape.playing ? tape.engine_version :
953                          level.game_version);
954
955   /* dynamically adjust element properties according to game engine version */
956   InitElementPropertiesEngine(game.engine_version);
957
958 #if 0
959   printf("level %d: level version == %06d\n", level_nr, level.game_version);
960   printf("          tape version == %06d [%s] [file: %06d]\n",
961          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
962          tape.file_version);
963   printf("       => game.engine_version == %06d\n", game.engine_version);
964 #endif
965
966   /* ---------- recursively resolve group elements ------------------------- */
967
968   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
969     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
970       element_info[i].in_group[j] = FALSE;
971
972   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
973     resolve_group_element(EL_GROUP_START + i, 0);
974
975   /* ---------- initialize player's initial move delay --------------------- */
976
977   /* dynamically adjust player properties according to game engine version */
978   game.initial_move_delay =
979     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
980      INITIAL_MOVE_DELAY_OFF);
981
982   /* dynamically adjust player properties according to level information */
983   game.initial_move_delay_value =
984     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
985
986   /* ---------- initialize player's initial push delay --------------------- */
987
988   /* dynamically adjust player properties according to game engine version */
989   game.initial_push_delay_value =
990     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
991
992   /* ---------- initialize changing elements ------------------------------- */
993
994   /* initialize changing elements information */
995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
996   {
997     struct ElementInfo *ei = &element_info[i];
998
999     /* this pointer might have been changed in the level editor */
1000     ei->change = &ei->change_page[0];
1001
1002     if (!IS_CUSTOM_ELEMENT(i))
1003     {
1004       ei->change->target_element = EL_EMPTY_SPACE;
1005       ei->change->delay_fixed = 0;
1006       ei->change->delay_random = 0;
1007       ei->change->delay_frames = 1;
1008     }
1009
1010     ei->change_events = CE_BITMASK_DEFAULT;
1011     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1012     {
1013       ei->event_page_nr[j] = 0;
1014       ei->event_page[j] = &ei->change_page[0];
1015     }
1016   }
1017
1018   /* add changing elements from pre-defined list */
1019   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1020   {
1021     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1022     struct ElementInfo *ei = &element_info[ch_delay->element];
1023
1024     ei->change->target_element       = ch_delay->target_element;
1025     ei->change->delay_fixed          = ch_delay->change_delay;
1026
1027     ei->change->pre_change_function  = ch_delay->pre_change_function;
1028     ei->change->change_function      = ch_delay->change_function;
1029     ei->change->post_change_function = ch_delay->post_change_function;
1030
1031     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1032   }
1033
1034 #if 1
1035   /* add change events from custom element configuration */
1036   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1037   {
1038     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1039
1040     for (j = 0; j < ei->num_change_pages; j++)
1041     {
1042       if (!ei->change_page[j].can_change)
1043         continue;
1044
1045       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1046       {
1047         /* only add event page for the first page found with this event */
1048         if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1049             !(ei->change_events & CH_EVENT_BIT(k)))
1050         {
1051           ei->change_events |= CH_EVENT_BIT(k);
1052           ei->event_page_nr[k] = j;
1053           ei->event_page[k] = &ei->change_page[j];
1054         }
1055       }
1056     }
1057   }
1058
1059 #else
1060
1061   /* add change events from custom element configuration */
1062   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1063   {
1064     int element = EL_CUSTOM_START + i;
1065
1066     /* only add custom elements that change after fixed/random frame delay */
1067     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1068       element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1069   }
1070 #endif
1071
1072   /* ---------- initialize trigger events ---------------------------------- */
1073
1074   /* initialize trigger events information */
1075   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1076     trigger_events[i] = EP_BITMASK_DEFAULT;
1077
1078 #if 1
1079   /* add trigger events from element change event properties */
1080   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1081   {
1082     struct ElementInfo *ei = &element_info[i];
1083
1084     for (j = 0; j < ei->num_change_pages; j++)
1085     {
1086       if (!ei->change_page[j].can_change)
1087         continue;
1088
1089       if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1090       {
1091         int trigger_element = ei->change_page[j].trigger_element;
1092
1093         if (IS_GROUP_ELEMENT(trigger_element))
1094         {
1095           struct ElementGroupInfo *group = element_info[trigger_element].group;
1096
1097           for (k = 0; k < group->num_elements_resolved; k++)
1098             trigger_events[group->element_resolved[k]]
1099               |= ei->change_page[j].events;
1100         }
1101         else
1102           trigger_events[trigger_element] |= ei->change_page[j].events;
1103       }
1104     }
1105   }
1106 #else
1107   /* add trigger events from element change event properties */
1108   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1109     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1110       trigger_events[element_info[i].change->trigger_element] |=
1111         element_info[i].change->events;
1112 #endif
1113
1114   /* ---------- initialize push delay -------------------------------------- */
1115
1116   /* initialize push delay values to default */
1117   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1118   {
1119     if (!IS_CUSTOM_ELEMENT(i))
1120     {
1121       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1122       element_info[i].push_delay_random = game.default_push_delay_random;
1123     }
1124   }
1125
1126   /* set push delay value for certain elements from pre-defined list */
1127   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1128   {
1129     int e = push_delay_list[i].element;
1130
1131     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1132     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1133   }
1134
1135   /* set push delay value for Supaplex elements for newer engine versions */
1136   if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1137   {
1138     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1139     {
1140       if (IS_SP_ELEMENT(i))
1141       {
1142         element_info[i].push_delay_fixed  = 6;
1143         element_info[i].push_delay_random = 0;
1144       }
1145     }
1146   }
1147
1148   /* ---------- initialize move stepsize ----------------------------------- */
1149
1150   /* initialize move stepsize values to default */
1151   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1152     if (!IS_CUSTOM_ELEMENT(i))
1153       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1154
1155   /* set move stepsize value for certain elements from pre-defined list */
1156   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1157   {
1158     int e = move_stepsize_list[i].element;
1159
1160     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1161   }
1162
1163   /* ---------- initialize move dig/leave ---------------------------------- */
1164
1165   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1166   {
1167     element_info[i].can_leave_element = FALSE;
1168     element_info[i].can_leave_element_last = FALSE;
1169   }
1170
1171   /* ---------- initialize gem count --------------------------------------- */
1172
1173   /* initialize gem count values for each element */
1174   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1175     if (!IS_CUSTOM_ELEMENT(i))
1176       element_info[i].collect_count = 0;
1177
1178   /* add gem count values for all elements from pre-defined list */
1179   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1180     element_info[collect_count_list[i].element].collect_count =
1181       collect_count_list[i].count;
1182
1183   /* ---------- initialize access direction -------------------------------- */
1184
1185   /* initialize access direction values to default */
1186   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1187     if (!IS_CUSTOM_ELEMENT(i))
1188       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1189
1190   /* set access direction value for certain elements from pre-defined list */
1191   for (i = 0; tube_access[i].element != EL_UNDEFINED; i++)
1192     element_info[tube_access[i].element].access_direction =
1193       tube_access[i].direction;
1194 }
1195
1196
1197 /*
1198   =============================================================================
1199   InitGame()
1200   -----------------------------------------------------------------------------
1201   initialize and start new game
1202   =============================================================================
1203 */
1204
1205 void InitGame()
1206 {
1207   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
1208   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
1209   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
1210   int i, j, k, x, y;
1211
1212   InitGameEngine();
1213
1214 #if 0
1215 #if DEBUG
1216 #if USE_NEW_AMOEBA_CODE
1217   printf("Using new amoeba code.\n");
1218 #else
1219   printf("Using old amoeba code.\n");
1220 #endif
1221 #endif
1222 #endif
1223
1224   /* don't play tapes over network */
1225   network_playing = (options.network && !tape.playing);
1226
1227   for (i = 0; i < MAX_PLAYERS; i++)
1228   {
1229     struct PlayerInfo *player = &stored_player[i];
1230
1231     player->index_nr = i;
1232     player->element_nr = EL_PLAYER_1 + i;
1233
1234     player->present = FALSE;
1235     player->active = FALSE;
1236
1237     player->action = 0;
1238     player->effective_action = 0;
1239     player->programmed_action = 0;
1240
1241     player->score = 0;
1242     player->gems_still_needed = level.gems_needed;
1243     player->sokobanfields_still_needed = 0;
1244     player->lights_still_needed = 0;
1245     player->friends_still_needed = 0;
1246
1247     for (j = 0; j < 4; j++)
1248       player->key[j] = FALSE;
1249
1250     player->dynabomb_count = 0;
1251     player->dynabomb_size = 1;
1252     player->dynabombs_left = 0;
1253     player->dynabomb_xl = FALSE;
1254
1255     player->MovDir = MV_NO_MOVING;
1256     player->MovPos = 0;
1257     player->GfxPos = 0;
1258     player->GfxDir = MV_NO_MOVING;
1259     player->GfxAction = ACTION_DEFAULT;
1260     player->Frame = 0;
1261     player->StepFrame = 0;
1262
1263     player->use_murphy_graphic = FALSE;
1264
1265     player->block_last_field = FALSE;
1266
1267     player->actual_frame_counter = 0;
1268
1269     player->step_counter = 0;
1270
1271     player->last_move_dir = MV_NO_MOVING;
1272
1273     player->is_waiting = FALSE;
1274     player->is_moving = FALSE;
1275     player->is_digging = FALSE;
1276     player->is_snapping = FALSE;
1277     player->is_collecting = FALSE;
1278     player->is_pushing = FALSE;
1279     player->is_switching = FALSE;
1280     player->is_dropping = FALSE;
1281
1282     player->is_bored = FALSE;
1283     player->is_sleeping = FALSE;
1284
1285     player->frame_counter_bored = -1;
1286     player->frame_counter_sleeping = -1;
1287
1288     player->anim_delay_counter = 0;
1289     player->post_delay_counter = 0;
1290
1291     player->action_waiting = ACTION_DEFAULT;
1292     player->last_action_waiting = ACTION_DEFAULT;
1293     player->special_action_bored = ACTION_DEFAULT;
1294     player->special_action_sleeping = ACTION_DEFAULT;
1295
1296     player->num_special_action_bored = 0;
1297     player->num_special_action_sleeping = 0;
1298
1299     /* determine number of special actions for bored and sleeping animation */
1300     for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1301     {
1302       boolean found = FALSE;
1303
1304       for (k = 0; k < NUM_DIRECTIONS; k++)
1305         if (el_act_dir2img(player->element_nr, j, k) !=
1306             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1307           found = TRUE;
1308
1309       if (found)
1310         player->num_special_action_bored++;
1311       else
1312         break;
1313     }
1314     for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1315     {
1316       boolean found = FALSE;
1317
1318       for (k = 0; k < NUM_DIRECTIONS; k++)
1319         if (el_act_dir2img(player->element_nr, j, k) !=
1320             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1321           found = TRUE;
1322
1323       if (found)
1324         player->num_special_action_sleeping++;
1325       else
1326         break;
1327     }
1328
1329     player->switch_x = -1;
1330     player->switch_y = -1;
1331
1332     player->show_envelope = 0;
1333
1334     player->move_delay       = game.initial_move_delay;
1335     player->move_delay_value = game.initial_move_delay_value;
1336
1337     player->move_delay_reset_counter = 0;
1338
1339     player->push_delay = 0;
1340     player->push_delay_value = game.initial_push_delay_value;
1341
1342     player->drop_delay = 0;
1343
1344     player->last_jx = player->last_jy = 0;
1345     player->jx = player->jy = 0;
1346
1347     player->shield_normal_time_left = 0;
1348     player->shield_deadly_time_left = 0;
1349
1350     player->inventory_size = 0;
1351
1352     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1353     SnapField(player, 0, 0);
1354
1355     player->LevelSolved = FALSE;
1356     player->GameOver = FALSE;
1357   }
1358
1359   network_player_action_received = FALSE;
1360
1361 #if defined(PLATFORM_UNIX)
1362   /* initial null action */
1363   if (network_playing)
1364     SendToServer_MovePlayer(MV_NO_MOVING);
1365 #endif
1366
1367   ZX = ZY = -1;
1368
1369   FrameCounter = 0;
1370   TimeFrames = 0;
1371   TimePlayed = 0;
1372   TimeLeft = level.time;
1373
1374   ScreenMovDir = MV_NO_MOVING;
1375   ScreenMovPos = 0;
1376   ScreenGfxPos = 0;
1377
1378   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
1379
1380   AllPlayersGone = FALSE;
1381
1382   game.yamyam_content_nr = 0;
1383   game.magic_wall_active = FALSE;
1384   game.magic_wall_time_left = 0;
1385   game.light_time_left = 0;
1386   game.timegate_time_left = 0;
1387   game.switchgate_pos = 0;
1388   game.balloon_dir = MV_NO_MOVING;
1389   game.gravity = level.initial_gravity;
1390   game.explosions_delayed = TRUE;
1391
1392   game.envelope_active = FALSE;
1393
1394   for (i = 0; i < 4; i++)
1395   {
1396     game.belt_dir[i] = MV_NO_MOVING;
1397     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
1398   }
1399
1400   for (i = 0; i < MAX_NUM_AMOEBA; i++)
1401     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1402
1403   for (x = 0; x < lev_fieldx; x++)
1404   {
1405     for (y = 0; y < lev_fieldy; y++)
1406     {
1407       Feld[x][y] = level.field[x][y];
1408       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1409       ChangeDelay[x][y] = 0;
1410       ChangePage[x][y] = -1;
1411       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1412       AmoebaNr[x][y] = 0;
1413       WasJustMoving[x][y] = 0;
1414       WasJustFalling[x][y] = 0;
1415       Stop[x][y] = FALSE;
1416       Pushed[x][y] = FALSE;
1417
1418       Changed[x][y] = CE_BITMASK_DEFAULT;
1419       ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1420
1421       ExplodePhase[x][y] = 0;
1422       ExplodeDelay[x][y] = 0;
1423       ExplodeField[x][y] = EX_NO_EXPLOSION;
1424
1425       RunnerVisit[x][y] = 0;
1426       PlayerVisit[x][y] = 0;
1427
1428       GfxFrame[x][y] = 0;
1429       GfxRandom[x][y] = INIT_GFX_RANDOM();
1430       GfxElement[x][y] = EL_UNDEFINED;
1431       GfxAction[x][y] = ACTION_DEFAULT;
1432       GfxDir[x][y] = MV_NO_MOVING;
1433     }
1434   }
1435
1436   for (y = 0; y < lev_fieldy; y++)
1437   {
1438     for (x = 0; x < lev_fieldx; x++)
1439     {
1440       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1441         emulate_bd = FALSE;
1442       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1443         emulate_sb = FALSE;
1444       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1445         emulate_sp = FALSE;
1446
1447       InitField(x, y, TRUE);
1448     }
1449   }
1450
1451   InitBeltMovement();
1452
1453   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1454                     emulate_sb ? EMU_SOKOBAN :
1455                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1456
1457   /* initialize explosion and ignition delay */
1458   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1459   {
1460     if (!IS_CUSTOM_ELEMENT(i))
1461     {
1462       int num_phase = 9;
1463       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1464       int last_phase = num_phase * delay;
1465       int half_phase = (num_phase / 2) * delay;
1466
1467       element_info[i].explosion_delay = last_phase;
1468       element_info[i].ignition_delay = half_phase;
1469
1470       if (i == EL_BLACK_ORB)
1471         element_info[i].ignition_delay = 1;
1472     }
1473
1474     if (element_info[i].explosion_delay < 2)    /* !!! check again !!! */
1475       element_info[i].explosion_delay = 2;
1476
1477     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
1478       element_info[i].ignition_delay = 1;
1479   }
1480
1481   /* correct non-moving belts to start moving left */
1482   for (i = 0; i < 4; i++)
1483     if (game.belt_dir[i] == MV_NO_MOVING)
1484       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
1485
1486   /* check if any connected player was not found in playfield */
1487   for (i = 0; i < MAX_PLAYERS; i++)
1488   {
1489     struct PlayerInfo *player = &stored_player[i];
1490
1491     if (player->connected && !player->present)
1492     {
1493       for (j = 0; j < MAX_PLAYERS; j++)
1494       {
1495         struct PlayerInfo *some_player = &stored_player[j];
1496         int jx = some_player->jx, jy = some_player->jy;
1497
1498         /* assign first free player found that is present in the playfield */
1499         if (some_player->present && !some_player->connected)
1500         {
1501           player->present = TRUE;
1502           player->active = TRUE;
1503
1504           some_player->present = FALSE;
1505           some_player->active = FALSE;
1506
1507           StorePlayer[jx][jy] = player->element_nr;
1508           player->jx = player->last_jx = jx;
1509           player->jy = player->last_jy = jy;
1510
1511           break;
1512         }
1513       }
1514     }
1515   }
1516
1517   if (tape.playing)
1518   {
1519     /* when playing a tape, eliminate all players which do not participate */
1520
1521     for (i = 0; i < MAX_PLAYERS; i++)
1522     {
1523       if (stored_player[i].active && !tape.player_participates[i])
1524       {
1525         struct PlayerInfo *player = &stored_player[i];
1526         int jx = player->jx, jy = player->jy;
1527
1528         player->active = FALSE;
1529         StorePlayer[jx][jy] = 0;
1530         Feld[jx][jy] = EL_EMPTY;
1531       }
1532     }
1533   }
1534   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
1535   {
1536     /* when in single player mode, eliminate all but the first active player */
1537
1538     for (i = 0; i < MAX_PLAYERS; i++)
1539     {
1540       if (stored_player[i].active)
1541       {
1542         for (j = i + 1; j < MAX_PLAYERS; j++)
1543         {
1544           if (stored_player[j].active)
1545           {
1546             struct PlayerInfo *player = &stored_player[j];
1547             int jx = player->jx, jy = player->jy;
1548
1549             player->active = FALSE;
1550             player->present = FALSE;
1551
1552             StorePlayer[jx][jy] = 0;
1553             Feld[jx][jy] = EL_EMPTY;
1554           }
1555         }
1556       }
1557     }
1558   }
1559
1560   /* when recording the game, store which players take part in the game */
1561   if (tape.recording)
1562   {
1563     for (i = 0; i < MAX_PLAYERS; i++)
1564       if (stored_player[i].active)
1565         tape.player_participates[i] = TRUE;
1566   }
1567
1568   if (options.debug)
1569   {
1570     for (i = 0; i < MAX_PLAYERS; i++)
1571     {
1572       struct PlayerInfo *player = &stored_player[i];
1573
1574       printf("Player %d: present == %d, connected == %d, active == %d.\n",
1575              i+1,
1576              player->present,
1577              player->connected,
1578              player->active);
1579       if (local_player == player)
1580         printf("Player  %d is local player.\n", i+1);
1581     }
1582   }
1583
1584   if (BorderElement == EL_EMPTY)
1585   {
1586     SBX_Left = 0;
1587     SBX_Right = lev_fieldx - SCR_FIELDX;
1588     SBY_Upper = 0;
1589     SBY_Lower = lev_fieldy - SCR_FIELDY;
1590   }
1591   else
1592   {
1593     SBX_Left = -1;
1594     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1595     SBY_Upper = -1;
1596     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1597   }
1598
1599   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1600     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1601
1602   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1603     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1604
1605   /* if local player not found, look for custom element that might create
1606      the player (make some assumptions about the right custom element) */
1607   if (!local_player->present)
1608   {
1609     int start_x = 0, start_y = 0;
1610     int found_rating = 0;
1611     int found_element = EL_UNDEFINED;
1612
1613     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1614     {
1615       int element = Feld[x][y];
1616       int content;
1617       int xx, yy;
1618       boolean is_player;
1619
1620       if (!IS_CUSTOM_ELEMENT(element))
1621         continue;
1622
1623       if (CAN_CHANGE(element))
1624       {
1625         for (i = 0; i < element_info[element].num_change_pages; i++)
1626         {
1627           content = element_info[element].change_page[i].target_element;
1628           is_player = ELEM_IS_PLAYER(content);
1629
1630           if (is_player && (found_rating < 3 || element < found_element))
1631           {
1632             start_x = x;
1633             start_y = y;
1634
1635             found_rating = 3;
1636             found_element = element;
1637           }
1638         }
1639       }
1640
1641       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1642       {
1643         content = element_info[element].content[xx][yy];
1644         is_player = ELEM_IS_PLAYER(content);
1645
1646         if (is_player && (found_rating < 2 || element < found_element))
1647         {
1648           start_x = x + xx - 1;
1649           start_y = y + yy - 1;
1650
1651           found_rating = 2;
1652           found_element = element;
1653         }
1654
1655         if (!CAN_CHANGE(element))
1656           continue;
1657
1658         for (i = 0; i < element_info[element].num_change_pages; i++)
1659         {
1660           content = element_info[element].change_page[i].content[xx][yy];
1661           is_player = ELEM_IS_PLAYER(content);
1662
1663           if (is_player && (found_rating < 1 || element < found_element))
1664           {
1665             start_x = x + xx - 1;
1666             start_y = y + yy - 1;
1667
1668             found_rating = 1;
1669             found_element = element;
1670           }
1671         }
1672       }
1673     }
1674
1675     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
1676                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1677                 start_x - MIDPOSX);
1678
1679     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1680                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1681                 start_y - MIDPOSY);
1682   }
1683   else
1684   {
1685 #if 1
1686     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
1687                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1688                 local_player->jx - MIDPOSX);
1689
1690     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1691                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1692                 local_player->jy - MIDPOSY);
1693 #else
1694     scroll_x = SBX_Left;
1695     scroll_y = SBY_Upper;
1696     if (local_player->jx >= SBX_Left + MIDPOSX)
1697       scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1698                   local_player->jx - MIDPOSX :
1699                   SBX_Right);
1700     if (local_player->jy >= SBY_Upper + MIDPOSY)
1701       scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1702                   local_player->jy - MIDPOSY :
1703                   SBY_Lower);
1704 #endif
1705   }
1706
1707   CloseDoor(DOOR_CLOSE_1);
1708
1709   DrawLevel();
1710   DrawAllPlayers();
1711
1712   /* after drawing the level, correct some elements */
1713   if (game.timegate_time_left == 0)
1714     CloseAllOpenTimegates();
1715
1716   if (setup.soft_scrolling)
1717     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1718
1719   redraw_mask |= REDRAW_FROM_BACKBUFFER;
1720   FadeToFront();
1721
1722   /* copy default game door content to main double buffer */
1723   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1724              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1725
1726   if (level_nr < 100)
1727     DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1728   else
1729   {
1730     DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1731                 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1732     BlitBitmap(drawto, drawto,
1733                DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1734                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1735                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1736                DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1737   }
1738
1739   DrawGameDoorValues();
1740
1741   UnmapGameButtons();
1742   UnmapTapeButtons();
1743   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1744   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1745   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1746   MapGameButtons();
1747   MapTapeButtons();
1748
1749   /* copy actual game door content to door double buffer for OpenDoor() */
1750   BlitBitmap(drawto, bitmap_db_door,
1751              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1752
1753   OpenDoor(DOOR_OPEN_ALL);
1754
1755   PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1756
1757   if (setup.sound_music)
1758     PlayLevelMusic();
1759
1760   KeyboardAutoRepeatOffUnlessAutoplay();
1761
1762   if (options.debug)
1763   {
1764     for (i = 0; i < 4; i++)
1765       printf("Player %d %sactive.\n",
1766              i + 1, (stored_player[i].active ? "" : "not "));
1767   }
1768
1769 #if 0
1770   printf("::: starting game [%d]\n", FrameCounter);
1771 #endif
1772 }
1773
1774 void InitMovDir(int x, int y)
1775 {
1776   int i, element = Feld[x][y];
1777   static int xy[4][2] =
1778   {
1779     {  0, +1 },
1780     { +1,  0 },
1781     {  0, -1 },
1782     { -1,  0 }
1783   };
1784   static int direction[3][4] =
1785   {
1786     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
1787     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
1788     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
1789   };
1790
1791   switch(element)
1792   {
1793     case EL_BUG_RIGHT:
1794     case EL_BUG_UP:
1795     case EL_BUG_LEFT:
1796     case EL_BUG_DOWN:
1797       Feld[x][y] = EL_BUG;
1798       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1799       break;
1800
1801     case EL_SPACESHIP_RIGHT:
1802     case EL_SPACESHIP_UP:
1803     case EL_SPACESHIP_LEFT:
1804     case EL_SPACESHIP_DOWN:
1805       Feld[x][y] = EL_SPACESHIP;
1806       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1807       break;
1808
1809     case EL_BD_BUTTERFLY_RIGHT:
1810     case EL_BD_BUTTERFLY_UP:
1811     case EL_BD_BUTTERFLY_LEFT:
1812     case EL_BD_BUTTERFLY_DOWN:
1813       Feld[x][y] = EL_BD_BUTTERFLY;
1814       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1815       break;
1816
1817     case EL_BD_FIREFLY_RIGHT:
1818     case EL_BD_FIREFLY_UP:
1819     case EL_BD_FIREFLY_LEFT:
1820     case EL_BD_FIREFLY_DOWN:
1821       Feld[x][y] = EL_BD_FIREFLY;
1822       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1823       break;
1824
1825     case EL_PACMAN_RIGHT:
1826     case EL_PACMAN_UP:
1827     case EL_PACMAN_LEFT:
1828     case EL_PACMAN_DOWN:
1829       Feld[x][y] = EL_PACMAN;
1830       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1831       break;
1832
1833     case EL_SP_SNIKSNAK:
1834       MovDir[x][y] = MV_UP;
1835       break;
1836
1837     case EL_SP_ELECTRON:
1838       MovDir[x][y] = MV_LEFT;
1839       break;
1840
1841     case EL_MOLE_LEFT:
1842     case EL_MOLE_RIGHT:
1843     case EL_MOLE_UP:
1844     case EL_MOLE_DOWN:
1845       Feld[x][y] = EL_MOLE;
1846       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1847       break;
1848
1849     default:
1850       if (IS_CUSTOM_ELEMENT(element))
1851       {
1852         struct ElementInfo *ei = &element_info[element];
1853         int move_direction_initial = ei->move_direction_initial;
1854         int move_pattern = ei->move_pattern;
1855
1856         if (move_direction_initial == MV_START_PREVIOUS)
1857         {
1858           if (MovDir[x][y] != MV_NO_MOVING)
1859             return;
1860
1861           move_direction_initial = MV_START_AUTOMATIC;
1862         }
1863
1864         if (move_direction_initial == MV_START_RANDOM)
1865           MovDir[x][y] = 1 << RND(4);
1866         else if (move_direction_initial & MV_ANY_DIRECTION)
1867           MovDir[x][y] = move_direction_initial;
1868         else if (move_pattern == MV_ALL_DIRECTIONS ||
1869                  move_pattern == MV_TURNING_LEFT ||
1870                  move_pattern == MV_TURNING_RIGHT ||
1871                  move_pattern == MV_TURNING_LEFT_RIGHT ||
1872                  move_pattern == MV_TURNING_RIGHT_LEFT ||
1873                  move_pattern == MV_TURNING_RANDOM)
1874           MovDir[x][y] = 1 << RND(4);
1875         else if (move_pattern == MV_HORIZONTAL)
1876           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1877         else if (move_pattern == MV_VERTICAL)
1878           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1879         else if (move_pattern & MV_ANY_DIRECTION)
1880           MovDir[x][y] = element_info[element].move_pattern;
1881         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1882                  move_pattern == MV_ALONG_RIGHT_SIDE)
1883         {
1884           for (i = 0; i < 4; i++)
1885           {
1886             int x1 = x + xy[i][0];
1887             int y1 = y + xy[i][1];
1888
1889             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1890             {
1891               if (move_pattern == MV_ALONG_RIGHT_SIDE)
1892                 MovDir[x][y] = direction[0][i];
1893               else
1894                 MovDir[x][y] = direction[1][i];
1895
1896               break;
1897             }
1898           }
1899         }                
1900       }
1901       else
1902       {
1903         MovDir[x][y] = 1 << RND(4);
1904
1905         if (element != EL_BUG &&
1906             element != EL_SPACESHIP &&
1907             element != EL_BD_BUTTERFLY &&
1908             element != EL_BD_FIREFLY)
1909           break;
1910
1911         for (i = 0; i < 4; i++)
1912         {
1913           int x1 = x + xy[i][0];
1914           int y1 = y + xy[i][1];
1915
1916           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1917           {
1918             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1919             {
1920               MovDir[x][y] = direction[0][i];
1921               break;
1922             }
1923             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1924                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1925             {
1926               MovDir[x][y] = direction[1][i];
1927               break;
1928             }
1929           }
1930         }
1931       }
1932       break;
1933   }
1934
1935   GfxDir[x][y] = MovDir[x][y];
1936 }
1937
1938 void InitAmoebaNr(int x, int y)
1939 {
1940   int i;
1941   int group_nr = AmoebeNachbarNr(x, y);
1942
1943   if (group_nr == 0)
1944   {
1945     for (i = 1; i < MAX_NUM_AMOEBA; i++)
1946     {
1947       if (AmoebaCnt[i] == 0)
1948       {
1949         group_nr = i;
1950         break;
1951       }
1952     }
1953   }
1954
1955   AmoebaNr[x][y] = group_nr;
1956   AmoebaCnt[group_nr]++;
1957   AmoebaCnt2[group_nr]++;
1958 }
1959
1960 void GameWon()
1961 {
1962   int hi_pos;
1963   boolean raise_level = FALSE;
1964
1965   if (local_player->MovPos)
1966     return;
1967
1968 #if 1
1969   if (tape.auto_play)           /* tape might already be stopped here */
1970     tape.auto_play_level_solved = TRUE;
1971 #else
1972   if (tape.playing && tape.auto_play)
1973     tape.auto_play_level_solved = TRUE;
1974 #endif
1975
1976   local_player->LevelSolved = FALSE;
1977
1978   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1979
1980   if (TimeLeft)
1981   {
1982     if (!tape.playing && setup.sound_loops)
1983       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1984                    SND_CTRL_PLAY_LOOP);
1985
1986     while (TimeLeft > 0)
1987     {
1988       if (!tape.playing && !setup.sound_loops)
1989         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1990       if (TimeLeft > 0 && !(TimeLeft % 10))
1991         RaiseScore(level.score[SC_TIME_BONUS]);
1992       if (TimeLeft > 100 && !(TimeLeft % 10))
1993         TimeLeft -= 10;
1994       else
1995         TimeLeft--;
1996       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1997       BackToFront();
1998
1999       if (!tape.playing)
2000         Delay(10);
2001     }
2002
2003     if (!tape.playing && setup.sound_loops)
2004       StopSound(SND_GAME_LEVELTIME_BONUS);
2005   }
2006   else if (level.time == 0)             /* level without time limit */
2007   {
2008     if (!tape.playing && setup.sound_loops)
2009       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2010                    SND_CTRL_PLAY_LOOP);
2011
2012     while (TimePlayed < 999)
2013     {
2014       if (!tape.playing && !setup.sound_loops)
2015         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2016       if (TimePlayed < 999 && !(TimePlayed % 10))
2017         RaiseScore(level.score[SC_TIME_BONUS]);
2018       if (TimePlayed < 900 && !(TimePlayed % 10))
2019         TimePlayed += 10;
2020       else
2021         TimePlayed++;
2022       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
2023       BackToFront();
2024
2025       if (!tape.playing)
2026         Delay(10);
2027     }
2028
2029     if (!tape.playing && setup.sound_loops)
2030       StopSound(SND_GAME_LEVELTIME_BONUS);
2031   }
2032
2033   /* close exit door after last player */
2034   if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2035        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2036   {
2037     int element = Feld[ExitX][ExitY];
2038
2039     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2040                           EL_SP_EXIT_CLOSING);
2041
2042     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2043   }
2044
2045   /* Hero disappears */
2046   DrawLevelField(ExitX, ExitY);
2047   BackToFront();
2048
2049   if (tape.playing)
2050     return;
2051
2052   CloseDoor(DOOR_CLOSE_1);
2053
2054   if (tape.recording)
2055   {
2056     TapeStop();
2057     SaveTape(tape.level_nr);            /* Ask to save tape */
2058   }
2059
2060   if (level_nr == leveldir_current->handicap_level)
2061   {
2062     leveldir_current->handicap_level++;
2063     SaveLevelSetup_SeriesInfo();
2064   }
2065
2066   if (level_editor_test_game)
2067     local_player->score = -1;   /* no highscore when playing from editor */
2068   else if (level_nr < leveldir_current->last_level)
2069     raise_level = TRUE;         /* advance to next level */
2070
2071   if ((hi_pos = NewHiScore()) >= 0) 
2072   {
2073     game_status = GAME_MODE_SCORES;
2074     DrawHallOfFame(hi_pos);
2075     if (raise_level)
2076     {
2077       level_nr++;
2078       TapeErase();
2079     }
2080   }
2081   else
2082   {
2083     game_status = GAME_MODE_MAIN;
2084     if (raise_level)
2085     {
2086       level_nr++;
2087       TapeErase();
2088     }
2089     DrawMainMenu();
2090   }
2091
2092   BackToFront();
2093 }
2094
2095 int NewHiScore()
2096 {
2097   int k, l;
2098   int position = -1;
2099
2100   LoadScore(level_nr);
2101
2102   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2103       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
2104     return -1;
2105
2106   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
2107   {
2108     if (local_player->score > highscore[k].Score)
2109     {
2110       /* player has made it to the hall of fame */
2111
2112       if (k < MAX_SCORE_ENTRIES - 1)
2113       {
2114         int m = MAX_SCORE_ENTRIES - 1;
2115
2116 #ifdef ONE_PER_NAME
2117         for (l = k; l < MAX_SCORE_ENTRIES; l++)
2118           if (!strcmp(setup.player_name, highscore[l].Name))
2119             m = l;
2120         if (m == k)     /* player's new highscore overwrites his old one */
2121           goto put_into_list;
2122 #endif
2123
2124         for (l = m; l > k; l--)
2125         {
2126           strcpy(highscore[l].Name, highscore[l - 1].Name);
2127           highscore[l].Score = highscore[l - 1].Score;
2128         }
2129       }
2130
2131 #ifdef ONE_PER_NAME
2132       put_into_list:
2133 #endif
2134       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2135       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2136       highscore[k].Score = local_player->score; 
2137       position = k;
2138       break;
2139     }
2140
2141 #ifdef ONE_PER_NAME
2142     else if (!strncmp(setup.player_name, highscore[k].Name,
2143                       MAX_PLAYER_NAME_LEN))
2144       break;    /* player already there with a higher score */
2145 #endif
2146
2147   }
2148
2149   if (position >= 0) 
2150     SaveScore(level_nr);
2151
2152   return position;
2153 }
2154
2155 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2156 {
2157   if (player->GfxAction != action || player->GfxDir != dir)
2158   {
2159 #if 0
2160     printf("Player frame reset! (%d => %d, %d => %d)\n",
2161            player->GfxAction, action, player->GfxDir, dir);
2162 #endif
2163
2164     player->GfxAction = action;
2165     player->GfxDir = dir;
2166     player->Frame = 0;
2167     player->StepFrame = 0;
2168   }
2169 }
2170
2171 static void ResetRandomAnimationValue(int x, int y)
2172 {
2173   GfxRandom[x][y] = INIT_GFX_RANDOM();
2174 }
2175
2176 static void ResetGfxAnimation(int x, int y)
2177 {
2178   GfxFrame[x][y] = 0;
2179   GfxAction[x][y] = ACTION_DEFAULT;
2180   GfxDir[x][y] = MovDir[x][y];
2181 }
2182
2183 void InitMovingField(int x, int y, int direction)
2184 {
2185   int element = Feld[x][y];
2186   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2187   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2188   int newx = x + dx;
2189   int newy = y + dy;
2190
2191   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2192     ResetGfxAnimation(x, y);
2193
2194   MovDir[newx][newy] = MovDir[x][y] = direction;
2195   GfxDir[x][y] = direction;
2196
2197   if (Feld[newx][newy] == EL_EMPTY)
2198     Feld[newx][newy] = EL_BLOCKED;
2199
2200   if (direction == MV_DOWN && CAN_FALL(element))
2201     GfxAction[x][y] = ACTION_FALLING;
2202   else
2203     GfxAction[x][y] = ACTION_MOVING;
2204
2205   GfxFrame[newx][newy] = GfxFrame[x][y];
2206   GfxRandom[newx][newy] = GfxRandom[x][y];
2207   GfxAction[newx][newy] = GfxAction[x][y];
2208   GfxDir[newx][newy] = GfxDir[x][y];
2209 }
2210
2211 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2212 {
2213   int direction = MovDir[x][y];
2214   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2215   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2216
2217   *goes_to_x = newx;
2218   *goes_to_y = newy;
2219 }
2220
2221 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2222 {
2223   int oldx = x, oldy = y;
2224   int direction = MovDir[x][y];
2225
2226   if (direction == MV_LEFT)
2227     oldx++;
2228   else if (direction == MV_RIGHT)
2229     oldx--;
2230   else if (direction == MV_UP)
2231     oldy++;
2232   else if (direction == MV_DOWN)
2233     oldy--;
2234
2235   *comes_from_x = oldx;
2236   *comes_from_y = oldy;
2237 }
2238
2239 int MovingOrBlocked2Element(int x, int y)
2240 {
2241   int element = Feld[x][y];
2242
2243   if (element == EL_BLOCKED)
2244   {
2245     int oldx, oldy;
2246
2247     Blocked2Moving(x, y, &oldx, &oldy);
2248     return Feld[oldx][oldy];
2249   }
2250   else
2251     return element;
2252 }
2253
2254 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2255 {
2256   /* like MovingOrBlocked2Element(), but if element is moving
2257      and (x,y) is the field the moving element is just leaving,
2258      return EL_BLOCKED instead of the element value */
2259   int element = Feld[x][y];
2260
2261   if (IS_MOVING(x, y))
2262   {
2263     if (element == EL_BLOCKED)
2264     {
2265       int oldx, oldy;
2266
2267       Blocked2Moving(x, y, &oldx, &oldy);
2268       return Feld[oldx][oldy];
2269     }
2270     else
2271       return EL_BLOCKED;
2272   }
2273   else
2274     return element;
2275 }
2276
2277 static void RemoveField(int x, int y)
2278 {
2279   Feld[x][y] = EL_EMPTY;
2280
2281   MovPos[x][y] = 0;
2282   MovDir[x][y] = 0;
2283   MovDelay[x][y] = 0;
2284
2285   AmoebaNr[x][y] = 0;
2286   ChangeDelay[x][y] = 0;
2287   ChangePage[x][y] = -1;
2288   Pushed[x][y] = FALSE;
2289
2290   GfxElement[x][y] = EL_UNDEFINED;
2291   GfxAction[x][y] = ACTION_DEFAULT;
2292   GfxDir[x][y] = MV_NO_MOVING;
2293 }
2294
2295 void RemoveMovingField(int x, int y)
2296 {
2297   int oldx = x, oldy = y, newx = x, newy = y;
2298   int element = Feld[x][y];
2299   int next_element = EL_UNDEFINED;
2300
2301   if (element != EL_BLOCKED && !IS_MOVING(x, y))
2302     return;
2303
2304   if (IS_MOVING(x, y))
2305   {
2306     Moving2Blocked(x, y, &newx, &newy);
2307     if (Feld[newx][newy] != EL_BLOCKED)
2308       return;
2309   }
2310   else if (element == EL_BLOCKED)
2311   {
2312     Blocked2Moving(x, y, &oldx, &oldy);
2313     if (!IS_MOVING(oldx, oldy))
2314       return;
2315   }
2316
2317   if (element == EL_BLOCKED &&
2318       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2319        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2320        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2321        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2322     next_element = get_next_element(Feld[oldx][oldy]);
2323
2324   RemoveField(oldx, oldy);
2325   RemoveField(newx, newy);
2326
2327   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2328
2329   if (next_element != EL_UNDEFINED)
2330     Feld[oldx][oldy] = next_element;
2331
2332   DrawLevelField(oldx, oldy);
2333   DrawLevelField(newx, newy);
2334 }
2335
2336 void DrawDynamite(int x, int y)
2337 {
2338   int sx = SCREENX(x), sy = SCREENY(y);
2339   int graphic = el2img(Feld[x][y]);
2340   int frame;
2341
2342   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2343     return;
2344
2345   if (IS_WALKABLE_INSIDE(Back[x][y]))
2346     return;
2347
2348   if (Back[x][y])
2349     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2350   else if (Store[x][y])
2351     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2352
2353   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2354
2355 #if 1
2356   if (Back[x][y] || Store[x][y])
2357     DrawGraphicThruMask(sx, sy, graphic, frame);
2358   else
2359     DrawGraphic(sx, sy, graphic, frame);
2360 #else
2361   if (game.emulation == EMU_SUPAPLEX)
2362     DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2363   else if (Store[x][y])
2364     DrawGraphicThruMask(sx, sy, graphic, frame);
2365   else
2366     DrawGraphic(sx, sy, graphic, frame);
2367 #endif
2368 }
2369
2370 void CheckDynamite(int x, int y)
2371 {
2372   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
2373   {
2374     MovDelay[x][y]--;
2375
2376     if (MovDelay[x][y] != 0)
2377     {
2378       DrawDynamite(x, y);
2379       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2380
2381       return;
2382     }
2383   }
2384
2385 #if 1
2386   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2387 #else
2388   if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2389       Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2390     StopSound(SND_DYNAMITE_ACTIVE);
2391   else
2392     StopSound(SND_DYNABOMB_ACTIVE);
2393 #endif
2394
2395   Bang(x, y);
2396 }
2397
2398 void RelocatePlayer(int x, int y, int element_raw)
2399 {
2400   int element = (element_raw == EL_SP_MURPHY ? EL_PLAYER_1 : element_raw);
2401   struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2402   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2403   boolean no_delay = (tape.index_search);
2404   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2405   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2406
2407   if (player->GameOver)         /* do not reanimate dead player */
2408     return;
2409
2410 #if 1
2411   RemoveField(x, y);            /* temporarily remove newly placed player */
2412   DrawLevelField(x, y);
2413 #endif
2414
2415   if (player->present)
2416   {
2417     while (player->MovPos)
2418     {
2419       ScrollPlayer(player, SCROLL_GO_ON);
2420       ScrollScreen(NULL, SCROLL_GO_ON);
2421       FrameCounter++;
2422
2423       DrawPlayer(player);
2424
2425       BackToFront();
2426       Delay(wait_delay_value);
2427     }
2428
2429     DrawPlayer(player);         /* needed here only to cleanup last field */
2430     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
2431
2432     player->is_moving = FALSE;
2433   }
2434
2435   Feld[x][y] = element;
2436   InitPlayerField(x, y, element, TRUE);
2437
2438   if (player == local_player)
2439   {
2440     int scroll_xx = -999, scroll_yy = -999;
2441
2442     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2443     {
2444       int dx = 0, dy = 0;
2445       int fx = FX, fy = FY;
2446
2447       scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2448                    local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2449                    local_player->jx - MIDPOSX);
2450
2451       scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2452                    local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2453                    local_player->jy - MIDPOSY);
2454
2455       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2456       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2457
2458       scroll_x -= dx;
2459       scroll_y -= dy;
2460
2461       fx += dx * TILEX / 2;
2462       fy += dy * TILEY / 2;
2463
2464       ScrollLevel(dx, dy);
2465       DrawAllPlayers();
2466
2467       /* scroll in two steps of half tile size to make things smoother */
2468       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2469       FlushDisplay();
2470       Delay(wait_delay_value);
2471
2472       /* scroll second step to align at full tile size */
2473       BackToFront();
2474       Delay(wait_delay_value);
2475     }
2476   }
2477 }
2478
2479 void Explode(int ex, int ey, int phase, int mode)
2480 {
2481   int x, y;
2482 #if 0
2483   int num_phase = 9;
2484 #endif
2485
2486   /* !!! eliminate this variable !!! */
2487   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2488
2489 #if 1
2490   int last_phase;
2491 #else
2492   int last_phase = num_phase * delay;
2493   int half_phase = (num_phase / 2) * delay;
2494   int first_phase_after_start = EX_PHASE_START + 1;
2495 #endif
2496   int border_element;
2497
2498   if (game.explosions_delayed)
2499   {
2500     ExplodeField[ex][ey] = mode;
2501     return;
2502   }
2503
2504   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
2505   {
2506     int center_element = Feld[ex][ey];
2507
2508 #if 0
2509     /* --- This is only really needed (and now handled) in "Impact()". --- */
2510     /* do not explode moving elements that left the explode field in time */
2511     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2512         center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2513       return;
2514 #endif
2515
2516     if (mode == EX_NORMAL || mode == EX_CENTER)
2517       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2518
2519     /* remove things displayed in background while burning dynamite */
2520     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2521       Back[ex][ey] = 0;
2522
2523     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2524     {
2525       /* put moving element to center field (and let it explode there) */
2526       center_element = MovingOrBlocked2Element(ex, ey);
2527       RemoveMovingField(ex, ey);
2528       Feld[ex][ey] = center_element;
2529     }
2530
2531 #if 1
2532     last_phase = element_info[center_element].explosion_delay;
2533 #endif
2534
2535     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2536     {
2537       int xx = x - ex + 1;
2538       int yy = y - ey + 1;
2539       int element;
2540
2541       if (!IN_LEV_FIELD(x, y) ||
2542           ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2543            (x != ex || y != ey)))
2544         continue;
2545
2546       element = Feld[x][y];
2547
2548       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2549       {
2550         element = MovingOrBlocked2Element(x, y);
2551
2552         if (!IS_EXPLOSION_PROOF(element))
2553           RemoveMovingField(x, y);
2554       }
2555
2556 #if 1
2557
2558 #if 0
2559       if (IS_EXPLOSION_PROOF(element))
2560         continue;
2561 #else
2562       /* indestructible elements can only explode in center (but not flames) */
2563       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2564           element == EL_FLAMES)
2565         continue;
2566 #endif
2567
2568 #else
2569       if ((IS_INDESTRUCTIBLE(element) &&
2570            (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2571             (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2572           element == EL_FLAMES)
2573         continue;
2574 #endif
2575
2576       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2577       {
2578         if (IS_ACTIVE_BOMB(element))
2579         {
2580           /* re-activate things under the bomb like gate or penguin */
2581           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2582           Store[x][y] = 0;
2583         }
2584
2585         continue;
2586       }
2587
2588       /* save walkable background elements while explosion on same tile */
2589 #if 0
2590       if (IS_INDESTRUCTIBLE(element))
2591         Back[x][y] = element;
2592 #else
2593       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2594         Back[x][y] = element;
2595 #endif
2596
2597       /* ignite explodable elements reached by other explosion */
2598       if (element == EL_EXPLOSION)
2599         element = Store2[x][y];
2600
2601 #if 1
2602       if (AmoebaNr[x][y] &&
2603           (element == EL_AMOEBA_FULL ||
2604            element == EL_BD_AMOEBA ||
2605            element == EL_AMOEBA_GROWING))
2606       {
2607         AmoebaCnt[AmoebaNr[x][y]]--;
2608         AmoebaCnt2[AmoebaNr[x][y]]--;
2609       }
2610
2611       RemoveField(x, y);
2612 #endif
2613
2614       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2615       {
2616         switch(StorePlayer[ex][ey])
2617         {
2618           case EL_PLAYER_2:
2619             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2620             break;
2621           case EL_PLAYER_3:
2622             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2623             break;
2624           case EL_PLAYER_4:
2625             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2626             break;
2627           case EL_PLAYER_1:
2628           default:
2629             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2630             break;
2631         }
2632
2633         if (game.emulation == EMU_SUPAPLEX)
2634           Store[x][y] = EL_EMPTY;
2635       }
2636       else if (center_element == EL_MOLE)
2637         Store[x][y] = EL_EMERALD_RED;
2638       else if (center_element == EL_PENGUIN)
2639         Store[x][y] = EL_EMERALD_PURPLE;
2640       else if (center_element == EL_BUG)
2641         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2642       else if (center_element == EL_BD_BUTTERFLY)
2643         Store[x][y] = EL_BD_DIAMOND;
2644       else if (center_element == EL_SP_ELECTRON)
2645         Store[x][y] = EL_SP_INFOTRON;
2646       else if (center_element == EL_AMOEBA_TO_DIAMOND)
2647         Store[x][y] = level.amoeba_content;
2648       else if (center_element == EL_YAMYAM)
2649         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2650       else if (IS_CUSTOM_ELEMENT(center_element) &&
2651                element_info[center_element].content[xx][yy] != EL_EMPTY)
2652         Store[x][y] = element_info[center_element].content[xx][yy];
2653       else if (element == EL_WALL_EMERALD)
2654         Store[x][y] = EL_EMERALD;
2655       else if (element == EL_WALL_DIAMOND)
2656         Store[x][y] = EL_DIAMOND;
2657       else if (element == EL_WALL_BD_DIAMOND)
2658         Store[x][y] = EL_BD_DIAMOND;
2659       else if (element == EL_WALL_EMERALD_YELLOW)
2660         Store[x][y] = EL_EMERALD_YELLOW;
2661       else if (element == EL_WALL_EMERALD_RED)
2662         Store[x][y] = EL_EMERALD_RED;
2663       else if (element == EL_WALL_EMERALD_PURPLE)
2664         Store[x][y] = EL_EMERALD_PURPLE;
2665       else if (element == EL_WALL_PEARL)
2666         Store[x][y] = EL_PEARL;
2667       else if (element == EL_WALL_CRYSTAL)
2668         Store[x][y] = EL_CRYSTAL;
2669       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2670         Store[x][y] = element_info[element].content[1][1];
2671       else
2672         Store[x][y] = EL_EMPTY;
2673
2674       if (x != ex || y != ey ||
2675           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2676         Store2[x][y] = element;
2677
2678 #if 0
2679       if (AmoebaNr[x][y] &&
2680           (element == EL_AMOEBA_FULL ||
2681            element == EL_BD_AMOEBA ||
2682            element == EL_AMOEBA_GROWING))
2683       {
2684         AmoebaCnt[AmoebaNr[x][y]]--;
2685         AmoebaCnt2[AmoebaNr[x][y]]--;
2686       }
2687
2688 #if 1
2689       RemoveField(x, y);
2690 #else
2691       MovDir[x][y] = MovPos[x][y] = 0;
2692       GfxDir[x][y] = MovDir[x][y];
2693       AmoebaNr[x][y] = 0;
2694 #endif
2695 #endif
2696
2697       Feld[x][y] = EL_EXPLOSION;
2698 #if 1
2699       GfxElement[x][y] = center_element;
2700 #else
2701       GfxElement[x][y] = EL_UNDEFINED;
2702 #endif
2703
2704       ExplodePhase[x][y] = 1;
2705 #if 1
2706       ExplodeDelay[x][y] = last_phase;
2707 #endif
2708       Stop[x][y] = TRUE;
2709     }
2710
2711     if (center_element == EL_YAMYAM)
2712       game.yamyam_content_nr =
2713         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2714
2715     return;
2716   }
2717
2718   if (Stop[ex][ey])
2719     return;
2720
2721   x = ex;
2722   y = ey;
2723
2724 #if 1
2725   last_phase = ExplodeDelay[x][y];
2726 #endif
2727
2728   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2729
2730 #ifdef DEBUG
2731
2732   /* activate this even in non-DEBUG version until cause for crash in
2733      getGraphicAnimationFrame() (see below) is found and eliminated */
2734 #endif
2735 #if 1
2736
2737   if (GfxElement[x][y] == EL_UNDEFINED)
2738   {
2739     printf("\n\n");
2740     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2741     printf("Explode(): This should never happen!\n");
2742     printf("\n\n");
2743
2744     GfxElement[x][y] = EL_EMPTY;
2745   }
2746 #endif
2747
2748 #if 1
2749
2750   border_element = Store2[x][y];
2751   if (IS_PLAYER(x, y))
2752     border_element = StorePlayer[x][y];
2753
2754   if (phase == element_info[border_element].ignition_delay ||
2755       phase == last_phase)
2756   {
2757     boolean border_explosion = FALSE;
2758
2759 #if 1
2760     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
2761 #else
2762     if (IS_PLAYER(x, y))
2763 #endif
2764     {
2765       KillHeroUnlessExplosionProtected(x, y);
2766       border_explosion = TRUE;
2767
2768 #if 0
2769       if (phase == last_phase)
2770         printf("::: IS_PLAYER\n");
2771 #endif
2772     }
2773     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
2774     {
2775       Feld[x][y] = Store2[x][y];
2776       Store2[x][y] = 0;
2777       Bang(x, y);
2778       border_explosion = TRUE;
2779
2780 #if 0
2781       if (phase == last_phase)
2782         printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
2783 #endif
2784     }
2785     else if (border_element == EL_AMOEBA_TO_DIAMOND)
2786     {
2787       AmoebeUmwandeln(x, y);
2788       Store2[x][y] = 0;
2789       border_explosion = TRUE;
2790
2791 #if 0
2792       if (phase == last_phase)
2793         printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
2794                element_info[border_element].explosion_delay,
2795                element_info[border_element].ignition_delay,
2796                phase);
2797 #endif
2798     }
2799
2800 #if 1
2801     /* if an element just explodes due to another explosion (chain-reaction),
2802        do not immediately end the new explosion when it was the last frame of
2803        the explosion (as it would be done in the following "if"-statement!) */
2804     if (border_explosion && phase == last_phase)
2805       return;
2806 #endif
2807   }
2808
2809 #else
2810
2811   if (phase == first_phase_after_start)
2812   {
2813     int element = Store2[x][y];
2814
2815     if (element == EL_BLACK_ORB)
2816     {
2817       Feld[x][y] = Store2[x][y];
2818       Store2[x][y] = 0;
2819       Bang(x, y);
2820     }
2821   }
2822   else if (phase == half_phase)
2823   {
2824     int element = Store2[x][y];
2825
2826     if (IS_PLAYER(x, y))
2827       KillHeroUnlessExplosionProtected(x, y);
2828     else if (CAN_EXPLODE_BY_EXPLOSION(element))
2829     {
2830       Feld[x][y] = Store2[x][y];
2831       Store2[x][y] = 0;
2832       Bang(x, y);
2833     }
2834     else if (element == EL_AMOEBA_TO_DIAMOND)
2835       AmoebeUmwandeln(x, y);
2836   }
2837 #endif
2838
2839   if (phase == last_phase)
2840   {
2841     int element;
2842
2843     element = Feld[x][y] = Store[x][y];
2844     Store[x][y] = Store2[x][y] = 0;
2845     GfxElement[x][y] = EL_UNDEFINED;
2846
2847     /* player can escape from explosions and might therefore be still alive */
2848     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2849         element <= EL_PLAYER_IS_EXPLODING_4)
2850       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2851                     EL_EMPTY :
2852                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2853                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2854                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2855                     EL_EMERALD_PURPLE);
2856
2857     /* restore probably existing indestructible background element */
2858     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2859       element = Feld[x][y] = Back[x][y];
2860     Back[x][y] = 0;
2861
2862     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2863     GfxDir[x][y] = MV_NO_MOVING;
2864     ChangeDelay[x][y] = 0;
2865     ChangePage[x][y] = -1;
2866
2867     InitField(x, y, FALSE);
2868 #if 1
2869     /* !!! not needed !!! */
2870     if (CAN_MOVE(element))
2871       InitMovDir(x, y);
2872 #endif
2873     DrawLevelField(x, y);
2874
2875     TestIfElementTouchesCustomElement(x, y);
2876
2877     if (GFX_CRUMBLED(element))
2878       DrawLevelFieldCrumbledSandNeighbours(x, y);
2879
2880     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
2881       StorePlayer[x][y] = 0;
2882
2883     if (ELEM_IS_PLAYER(element))
2884       RelocatePlayer(x, y, element);
2885   }
2886 #if 1
2887   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2888 #else
2889   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2890 #endif
2891   {
2892 #if 1
2893     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2894 #else
2895     int stored = Store[x][y];
2896     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2897                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2898                    IMG_SP_EXPLOSION);
2899 #endif
2900     int frame = getGraphicAnimationFrame(graphic, phase - delay);
2901
2902 #if 0
2903     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2904            element_info[GfxElement[x][y]].token_name,
2905            graphic);
2906 #endif
2907
2908     if (phase == delay)
2909       DrawLevelFieldCrumbledSand(x, y);
2910
2911     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2912     {
2913       DrawLevelElement(x, y, Back[x][y]);
2914       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2915     }
2916     else if (IS_WALKABLE_UNDER(Back[x][y]))
2917     {
2918       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2919       DrawLevelElementThruMask(x, y, Back[x][y]);
2920     }
2921     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2922       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2923   }
2924 }
2925
2926 void DynaExplode(int ex, int ey)
2927 {
2928   int i, j;
2929   int dynabomb_element = Feld[ex][ey];
2930   int dynabomb_size = 1;
2931   boolean dynabomb_xl = FALSE;
2932   struct PlayerInfo *player;
2933   static int xy[4][2] =
2934   {
2935     { 0, -1 },
2936     { -1, 0 },
2937     { +1, 0 },
2938     { 0, +1 }
2939   };
2940
2941   if (IS_ACTIVE_BOMB(dynabomb_element))
2942   {
2943     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2944     dynabomb_size = player->dynabomb_size;
2945     dynabomb_xl = player->dynabomb_xl;
2946     player->dynabombs_left++;
2947   }
2948
2949   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2950
2951   for (i = 0; i < 4; i++)
2952   {
2953     for (j = 1; j <= dynabomb_size; j++)
2954     {
2955       int x = ex + j * xy[i % 4][0];
2956       int y = ey + j * xy[i % 4][1];
2957       int element;
2958
2959       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2960         break;
2961
2962       element = Feld[x][y];
2963
2964       /* do not restart explosions of fields with active bombs */
2965       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2966         continue;
2967
2968       Explode(x, y, EX_PHASE_START, EX_BORDER);
2969
2970       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2971       if (element != EL_EMPTY &&
2972           element != EL_SAND &&
2973           element != EL_EXPLOSION &&
2974           !dynabomb_xl)
2975         break;
2976     }
2977   }
2978 }
2979
2980 void Bang(int x, int y)
2981 {
2982 #if 1
2983   int element = MovingOrBlocked2Element(x, y);
2984 #else
2985   int element = Feld[x][y];
2986 #endif
2987
2988 #if 1
2989   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2990 #else
2991   if (IS_PLAYER(x, y))
2992 #endif
2993   {
2994     struct PlayerInfo *player = PLAYERINFO(x, y);
2995
2996     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2997                             player->element_nr);
2998   }
2999
3000 #if 0
3001 #if 1
3002   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3003 #else
3004   if (game.emulation == EMU_SUPAPLEX)
3005     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3006   else
3007     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3008 #endif
3009 #endif
3010
3011 #if 0
3012   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
3013     element = EL_EMPTY;
3014 #endif
3015
3016   switch(element)
3017   {
3018     case EL_BUG:
3019     case EL_SPACESHIP:
3020     case EL_BD_BUTTERFLY:
3021     case EL_BD_FIREFLY:
3022     case EL_YAMYAM:
3023     case EL_DARK_YAMYAM:
3024     case EL_ROBOT:
3025     case EL_PACMAN:
3026     case EL_MOLE:
3027       RaiseScoreElement(element);
3028       Explode(x, y, EX_PHASE_START, EX_NORMAL);
3029       break;
3030     case EL_DYNABOMB_PLAYER_1_ACTIVE:
3031     case EL_DYNABOMB_PLAYER_2_ACTIVE:
3032     case EL_DYNABOMB_PLAYER_3_ACTIVE:
3033     case EL_DYNABOMB_PLAYER_4_ACTIVE:
3034     case EL_DYNABOMB_INCREASE_NUMBER:
3035     case EL_DYNABOMB_INCREASE_SIZE:
3036     case EL_DYNABOMB_INCREASE_POWER:
3037       DynaExplode(x, y);
3038       break;
3039     case EL_PENGUIN:
3040     case EL_LAMP:
3041     case EL_LAMP_ACTIVE:
3042       if (IS_PLAYER(x, y))
3043         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3044       else
3045         Explode(x, y, EX_PHASE_START, EX_CENTER);
3046       break;
3047     default:
3048       if (CAN_EXPLODE_DYNA(element))
3049         DynaExplode(x, y);
3050       else if (CAN_EXPLODE_1X1(element))
3051         Explode(x, y, EX_PHASE_START, EX_CENTER);
3052       else
3053         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3054       break;
3055   }
3056
3057   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3058 }
3059
3060 void SplashAcid(int x, int y)
3061 {
3062   int element = Feld[x][y];
3063
3064   if (element != EL_ACID_SPLASH_LEFT &&
3065       element != EL_ACID_SPLASH_RIGHT)
3066   {
3067     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3068
3069     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3070         (!IN_LEV_FIELD(x-1, y-1) ||
3071          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3072       Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3073
3074     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3075         (!IN_LEV_FIELD(x+1, y-1) ||
3076          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3077       Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3078   }
3079 }
3080
3081 static void InitBeltMovement()
3082 {
3083   static int belt_base_element[4] =
3084   {
3085     EL_CONVEYOR_BELT_1_LEFT,
3086     EL_CONVEYOR_BELT_2_LEFT,
3087     EL_CONVEYOR_BELT_3_LEFT,
3088     EL_CONVEYOR_BELT_4_LEFT
3089   };
3090   static int belt_base_active_element[4] =
3091   {
3092     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3093     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3094     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3095     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3096   };
3097
3098   int x, y, i, j;
3099
3100   /* set frame order for belt animation graphic according to belt direction */
3101   for (i = 0; i < 4; i++)
3102   {
3103     int belt_nr = i;
3104
3105     for (j = 0; j < 3; j++)
3106     {
3107       int element = belt_base_active_element[belt_nr] + j;
3108       int graphic = el2img(element);
3109
3110       if (game.belt_dir[i] == MV_LEFT)
3111         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3112       else
3113         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3114     }
3115   }
3116
3117   for (y = 0; y < lev_fieldy; y++)
3118   {
3119     for (x = 0; x < lev_fieldx; x++)
3120     {
3121       int element = Feld[x][y];
3122
3123       for (i = 0; i < 4; i++)
3124       {
3125         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3126         {
3127           int e_belt_nr = getBeltNrFromBeltElement(element);
3128           int belt_nr = i;
3129
3130           if (e_belt_nr == belt_nr)
3131           {
3132             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3133
3134             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3135           }
3136         }
3137       }
3138     }
3139   }
3140 }
3141
3142 static void ToggleBeltSwitch(int x, int y)
3143 {
3144   static int belt_base_element[4] =
3145   {
3146     EL_CONVEYOR_BELT_1_LEFT,
3147     EL_CONVEYOR_BELT_2_LEFT,
3148     EL_CONVEYOR_BELT_3_LEFT,
3149     EL_CONVEYOR_BELT_4_LEFT
3150   };
3151   static int belt_base_active_element[4] =
3152   {
3153     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3154     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3155     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3156     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3157   };
3158   static int belt_base_switch_element[4] =
3159   {
3160     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3161     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3162     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3163     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3164   };
3165   static int belt_move_dir[4] =
3166   {
3167     MV_LEFT,
3168     MV_NO_MOVING,
3169     MV_RIGHT,
3170     MV_NO_MOVING,
3171   };
3172
3173   int element = Feld[x][y];
3174   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3175   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3176   int belt_dir = belt_move_dir[belt_dir_nr];
3177   int xx, yy, i;
3178
3179   if (!IS_BELT_SWITCH(element))
3180     return;
3181
3182   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3183   game.belt_dir[belt_nr] = belt_dir;
3184
3185   if (belt_dir_nr == 3)
3186     belt_dir_nr = 1;
3187
3188   /* set frame order for belt animation graphic according to belt direction */
3189   for (i = 0; i < 3; i++)
3190   {
3191     int element = belt_base_active_element[belt_nr] + i;
3192     int graphic = el2img(element);
3193
3194     if (belt_dir == MV_LEFT)
3195       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3196     else
3197       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3198   }
3199
3200   for (yy = 0; yy < lev_fieldy; yy++)
3201   {
3202     for (xx = 0; xx < lev_fieldx; xx++)
3203     {
3204       int element = Feld[xx][yy];
3205
3206       if (IS_BELT_SWITCH(element))
3207       {
3208         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3209
3210         if (e_belt_nr == belt_nr)
3211         {
3212           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3213           DrawLevelField(xx, yy);
3214         }
3215       }
3216       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3217       {
3218         int e_belt_nr = getBeltNrFromBeltElement(element);
3219
3220         if (e_belt_nr == belt_nr)
3221         {
3222           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3223
3224           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3225           DrawLevelField(xx, yy);
3226         }
3227       }
3228       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3229       {
3230         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3231
3232         if (e_belt_nr == belt_nr)
3233         {
3234           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3235
3236           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3237           DrawLevelField(xx, yy);
3238         }
3239       }
3240     }
3241   }
3242 }
3243
3244 static void ToggleSwitchgateSwitch(int x, int y)
3245 {
3246   int xx, yy;
3247
3248   game.switchgate_pos = !game.switchgate_pos;
3249
3250   for (yy = 0; yy < lev_fieldy; yy++)
3251   {
3252     for (xx = 0; xx < lev_fieldx; xx++)
3253     {
3254       int element = Feld[xx][yy];
3255
3256       if (element == EL_SWITCHGATE_SWITCH_UP ||
3257           element == EL_SWITCHGATE_SWITCH_DOWN)
3258       {
3259         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3260         DrawLevelField(xx, yy);
3261       }
3262       else if (element == EL_SWITCHGATE_OPEN ||
3263                element == EL_SWITCHGATE_OPENING)
3264       {
3265         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3266 #if 1
3267         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3268 #else
3269         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3270 #endif
3271       }
3272       else if (element == EL_SWITCHGATE_CLOSED ||
3273                element == EL_SWITCHGATE_CLOSING)
3274       {
3275         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3276 #if 1
3277         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3278 #else
3279         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3280 #endif
3281       }
3282     }
3283   }
3284 }
3285
3286 static int getInvisibleActiveFromInvisibleElement(int element)
3287 {
3288   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3289           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3290           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3291           element);
3292 }
3293
3294 static int getInvisibleFromInvisibleActiveElement(int element)
3295 {
3296   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3297           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3298           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3299           element);
3300 }
3301
3302 static void RedrawAllLightSwitchesAndInvisibleElements()
3303 {
3304   int x, y;
3305
3306   for (y = 0; y < lev_fieldy; y++)
3307   {
3308     for (x = 0; x < lev_fieldx; x++)
3309     {
3310       int element = Feld[x][y];
3311
3312       if (element == EL_LIGHT_SWITCH &&
3313           game.light_time_left > 0)
3314       {
3315         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3316         DrawLevelField(x, y);
3317       }
3318       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3319                game.light_time_left == 0)
3320       {
3321         Feld[x][y] = EL_LIGHT_SWITCH;
3322         DrawLevelField(x, y);
3323       }
3324       else if (element == EL_INVISIBLE_STEELWALL ||
3325                element == EL_INVISIBLE_WALL ||
3326                element == EL_INVISIBLE_SAND)
3327       {
3328         if (game.light_time_left > 0)
3329           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3330
3331         DrawLevelField(x, y);
3332       }
3333       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3334                element == EL_INVISIBLE_WALL_ACTIVE ||
3335                element == EL_INVISIBLE_SAND_ACTIVE)
3336       {
3337         if (game.light_time_left == 0)
3338           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3339
3340         DrawLevelField(x, y);
3341       }
3342     }
3343   }
3344 }
3345
3346 static void ToggleLightSwitch(int x, int y)
3347 {
3348   int element = Feld[x][y];
3349
3350   game.light_time_left =
3351     (element == EL_LIGHT_SWITCH ?
3352      level.time_light * FRAMES_PER_SECOND : 0);
3353
3354   RedrawAllLightSwitchesAndInvisibleElements();
3355 }
3356
3357 static void ActivateTimegateSwitch(int x, int y)
3358 {
3359   int xx, yy;
3360
3361   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3362
3363   for (yy = 0; yy < lev_fieldy; yy++)
3364   {
3365     for (xx = 0; xx < lev_fieldx; xx++)
3366     {
3367       int element = Feld[xx][yy];
3368
3369       if (element == EL_TIMEGATE_CLOSED ||
3370           element == EL_TIMEGATE_CLOSING)
3371       {
3372         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3373         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3374       }
3375
3376       /*
3377       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3378       {
3379         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3380         DrawLevelField(xx, yy);
3381       }
3382       */
3383
3384     }
3385   }
3386
3387   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3388 }
3389
3390 inline static int getElementMoveStepsize(int x, int y)
3391 {
3392   int element = Feld[x][y];
3393   int direction = MovDir[x][y];
3394   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3395   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3396   int horiz_move = (dx != 0);
3397   int sign = (horiz_move ? dx : dy);
3398   int step = sign * element_info[element].move_stepsize;
3399
3400   /* special values for move stepsize for spring and things on conveyor belt */
3401   if (horiz_move)
3402   {
3403 #if 1
3404     if (element == EL_SPRING)
3405       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3406     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3407              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3408       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3409 #else
3410     if (CAN_FALL(element) &&
3411         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3412       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3413     else if (element == EL_SPRING)
3414       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3415 #endif
3416   }
3417
3418   return step;
3419 }
3420
3421 void Impact(int x, int y)
3422 {
3423   boolean lastline = (y == lev_fieldy-1);
3424   boolean object_hit = FALSE;
3425   boolean impact = (lastline || object_hit);
3426   int element = Feld[x][y];
3427   int smashed = EL_UNDEFINED;
3428
3429   if (!lastline)        /* check if element below was hit */
3430   {
3431     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3432       return;
3433
3434     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3435                                          MovDir[x][y + 1] != MV_DOWN ||
3436                                          MovPos[x][y + 1] <= TILEY / 2));
3437
3438 #if 0
3439     object_hit = !IS_FREE(x, y + 1);
3440 #endif
3441
3442     /* do not smash moving elements that left the smashed field in time */
3443     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3444         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3445       object_hit = FALSE;
3446
3447     if (object_hit)
3448       smashed = MovingOrBlocked2Element(x, y + 1);
3449
3450     impact = (lastline || object_hit);
3451   }
3452
3453   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
3454   {
3455     SplashAcid(x, y);
3456     return;
3457   }
3458
3459   /* only reset graphic animation if graphic really changes after impact */
3460   if (impact &&
3461       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3462   {
3463     ResetGfxAnimation(x, y);
3464     DrawLevelField(x, y);
3465   }
3466
3467   if (impact && CAN_EXPLODE_IMPACT(element))
3468   {
3469     Bang(x, y);
3470     return;
3471   }
3472   else if (impact && element == EL_PEARL)
3473   {
3474     Feld[x][y] = EL_PEARL_BREAKING;
3475     PlayLevelSound(x, y, SND_PEARL_BREAKING);
3476     return;
3477   }
3478   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3479   {
3480     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3481
3482     return;
3483   }
3484
3485   if (impact && element == EL_AMOEBA_DROP)
3486   {
3487     if (object_hit && IS_PLAYER(x, y + 1))
3488       KillHeroUnlessEnemyProtected(x, y + 1);
3489     else if (object_hit && smashed == EL_PENGUIN)
3490       Bang(x, y + 1);
3491     else
3492     {
3493       Feld[x][y] = EL_AMOEBA_GROWING;
3494       Store[x][y] = EL_AMOEBA_WET;
3495
3496       ResetRandomAnimationValue(x, y);
3497     }
3498     return;
3499   }
3500
3501   if (object_hit)               /* check which object was hit */
3502   {
3503     if (CAN_PASS_MAGIC_WALL(element) && 
3504         (smashed == EL_MAGIC_WALL ||
3505          smashed == EL_BD_MAGIC_WALL))
3506     {
3507       int xx, yy;
3508       int activated_magic_wall =
3509         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3510          EL_BD_MAGIC_WALL_ACTIVE);
3511
3512       /* activate magic wall / mill */
3513       for (yy = 0; yy < lev_fieldy; yy++)
3514         for (xx = 0; xx < lev_fieldx; xx++)
3515           if (Feld[xx][yy] == smashed)
3516             Feld[xx][yy] = activated_magic_wall;
3517
3518       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3519       game.magic_wall_active = TRUE;
3520
3521       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3522                             SND_MAGIC_WALL_ACTIVATING :
3523                             SND_BD_MAGIC_WALL_ACTIVATING));
3524     }
3525
3526     if (IS_PLAYER(x, y + 1))
3527     {
3528       if (CAN_SMASH_PLAYER(element))
3529       {
3530         KillHeroUnlessEnemyProtected(x, y + 1);
3531         return;
3532       }
3533     }
3534     else if (smashed == EL_PENGUIN)
3535     {
3536       if (CAN_SMASH_PLAYER(element))
3537       {
3538         Bang(x, y + 1);
3539         return;
3540       }
3541     }
3542     else if (element == EL_BD_DIAMOND)
3543     {
3544       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3545       {
3546         Bang(x, y + 1);
3547         return;
3548       }
3549     }
3550     else if (((element == EL_SP_INFOTRON ||
3551                element == EL_SP_ZONK) &&
3552               (smashed == EL_SP_SNIKSNAK ||
3553                smashed == EL_SP_ELECTRON ||
3554                smashed == EL_SP_DISK_ORANGE)) ||
3555              (element == EL_SP_INFOTRON &&
3556               smashed == EL_SP_DISK_YELLOW))
3557     {
3558       Bang(x, y + 1);
3559       return;
3560     }
3561 #if 0
3562     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3563     {
3564       Bang(x, y + 1);
3565       return;
3566     }
3567 #endif
3568     else if (CAN_SMASH_EVERYTHING(element))
3569     {
3570       if (IS_CLASSIC_ENEMY(smashed) ||
3571           CAN_EXPLODE_SMASHED(smashed))
3572       {
3573         Bang(x, y + 1);
3574         return;
3575       }
3576       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3577       {
3578         if (smashed == EL_LAMP ||
3579             smashed == EL_LAMP_ACTIVE)
3580         {
3581           Bang(x, y + 1);
3582           return;
3583         }
3584         else if (smashed == EL_NUT)
3585         {
3586           Feld[x][y + 1] = EL_NUT_BREAKING;
3587           PlayLevelSound(x, y, SND_NUT_BREAKING);
3588           RaiseScoreElement(EL_NUT);
3589           return;
3590         }
3591         else if (smashed == EL_PEARL)
3592         {
3593           Feld[x][y + 1] = EL_PEARL_BREAKING;
3594           PlayLevelSound(x, y, SND_PEARL_BREAKING);
3595           return;
3596         }
3597         else if (smashed == EL_DIAMOND)
3598         {
3599           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3600           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3601           return;
3602         }
3603         else if (IS_BELT_SWITCH(smashed))
3604         {
3605           ToggleBeltSwitch(x, y + 1);
3606         }
3607         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3608                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
3609         {
3610           ToggleSwitchgateSwitch(x, y + 1);
3611         }
3612         else if (smashed == EL_LIGHT_SWITCH ||
3613                  smashed == EL_LIGHT_SWITCH_ACTIVE)
3614         {
3615           ToggleLightSwitch(x, y + 1);
3616         }
3617         else
3618         {
3619           CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3620
3621           CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3622                                           CE_OTHER_IS_SWITCHING);
3623           CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3624                                  CE_SWITCHED, -1);
3625         }
3626       }
3627       else
3628       {
3629         CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3630       }
3631     }
3632   }
3633
3634   /* play sound of magic wall / mill */
3635   if (!lastline &&
3636       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3637        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3638   {
3639     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3640       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3641     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3642       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3643
3644     return;
3645   }
3646
3647   /* play sound of object that hits the ground */
3648   if (lastline || object_hit)
3649     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3650 }
3651
3652 inline static void TurnRoundExt(int x, int y)
3653 {
3654   static struct
3655   {
3656     int x, y;
3657   } move_xy[] =
3658   {
3659     {  0,  0 },
3660     { -1,  0 },
3661     { +1,  0 },
3662     {  0,  0 },
3663     {  0, -1 },
3664     {  0,  0 }, { 0, 0 }, { 0, 0 },
3665     {  0, +1 }
3666   };
3667   static struct
3668   {
3669     int left, right, back;
3670   } turn[] =
3671   {
3672     { 0,        0,              0        },
3673     { MV_DOWN,  MV_UP,          MV_RIGHT },
3674     { MV_UP,    MV_DOWN,        MV_LEFT  },
3675     { 0,        0,              0        },
3676     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
3677     { 0,        0,              0        },
3678     { 0,        0,              0        },
3679     { 0,        0,              0        },
3680     { MV_RIGHT, MV_LEFT,        MV_UP    }
3681   };
3682
3683   int element = Feld[x][y];
3684   int move_pattern = element_info[element].move_pattern;
3685
3686   int old_move_dir = MovDir[x][y];
3687   int left_dir  = turn[old_move_dir].left;
3688   int right_dir = turn[old_move_dir].right;
3689   int back_dir  = turn[old_move_dir].back;
3690
3691   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
3692   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
3693   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
3694   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
3695
3696   int left_x  = x + left_dx,  left_y  = y + left_dy;
3697   int right_x = x + right_dx, right_y = y + right_dy;
3698   int move_x  = x + move_dx,  move_y  = y + move_dy;
3699
3700   int xx, yy;
3701
3702   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3703   {
3704     TestIfBadThingTouchesOtherBadThing(x, y);
3705
3706     if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3707       MovDir[x][y] = right_dir;
3708     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3709       MovDir[x][y] = left_dir;
3710
3711     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3712       MovDelay[x][y] = 9;
3713     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
3714       MovDelay[x][y] = 1;
3715   }
3716   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3717            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3718   {
3719     TestIfBadThingTouchesOtherBadThing(x, y);
3720
3721     if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3722       MovDir[x][y] = left_dir;
3723     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3724       MovDir[x][y] = right_dir;
3725
3726     if ((element == EL_SPACESHIP ||
3727          element == EL_SP_SNIKSNAK ||
3728          element == EL_SP_ELECTRON)
3729         && MovDir[x][y] != old_move_dir)
3730       MovDelay[x][y] = 9;
3731     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3732       MovDelay[x][y] = 1;
3733   }
3734   else if (element == EL_YAMYAM)
3735   {
3736     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3737     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3738
3739     if (can_turn_left && can_turn_right)
3740       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3741     else if (can_turn_left)
3742       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3743     else if (can_turn_right)
3744       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3745     else
3746       MovDir[x][y] = back_dir;
3747
3748     MovDelay[x][y] = 16 + 16 * RND(3);
3749   }
3750   else if (element == EL_DARK_YAMYAM)
3751   {
3752     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3753     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3754
3755     if (can_turn_left && can_turn_right)
3756       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3757     else if (can_turn_left)
3758       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3759     else if (can_turn_right)
3760       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3761     else
3762       MovDir[x][y] = back_dir;
3763
3764     MovDelay[x][y] = 16 + 16 * RND(3);
3765   }
3766   else if (element == EL_PACMAN)
3767   {
3768     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3769     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3770
3771     if (can_turn_left && can_turn_right)
3772       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3773     else if (can_turn_left)
3774       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3775     else if (can_turn_right)
3776       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3777     else
3778       MovDir[x][y] = back_dir;
3779
3780     MovDelay[x][y] = 6 + RND(40);
3781   }
3782   else if (element == EL_PIG)
3783   {
3784     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
3785     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3786     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
3787     boolean should_turn_left, should_turn_right, should_move_on;
3788     int rnd_value = 24;
3789     int rnd = RND(rnd_value);
3790
3791     should_turn_left = (can_turn_left &&
3792                         (!can_move_on ||
3793                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3794                                                    y + back_dy + left_dy)));
3795     should_turn_right = (can_turn_right &&
3796                          (!can_move_on ||
3797                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3798                                                     y + back_dy + right_dy)));
3799     should_move_on = (can_move_on &&
3800                       (!can_turn_left ||
3801                        !can_turn_right ||
3802                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3803                                                  y + move_dy + left_dy) ||
3804                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3805                                                  y + move_dy + right_dy)));
3806
3807     if (should_turn_left || should_turn_right || should_move_on)
3808     {
3809       if (should_turn_left && should_turn_right && should_move_on)
3810         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
3811                         rnd < 2 * rnd_value / 3 ? right_dir :
3812                         old_move_dir);
3813       else if (should_turn_left && should_turn_right)
3814         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3815       else if (should_turn_left && should_move_on)
3816         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3817       else if (should_turn_right && should_move_on)
3818         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3819       else if (should_turn_left)
3820         MovDir[x][y] = left_dir;
3821       else if (should_turn_right)
3822         MovDir[x][y] = right_dir;
3823       else if (should_move_on)
3824         MovDir[x][y] = old_move_dir;
3825     }
3826     else if (can_move_on && rnd > rnd_value / 8)
3827       MovDir[x][y] = old_move_dir;
3828     else if (can_turn_left && can_turn_right)
3829       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3830     else if (can_turn_left && rnd > rnd_value / 8)
3831       MovDir[x][y] = left_dir;
3832     else if (can_turn_right && rnd > rnd_value/8)
3833       MovDir[x][y] = right_dir;
3834     else
3835       MovDir[x][y] = back_dir;
3836
3837     xx = x + move_xy[MovDir[x][y]].x;
3838     yy = y + move_xy[MovDir[x][y]].y;
3839
3840     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3841       MovDir[x][y] = old_move_dir;
3842
3843     MovDelay[x][y] = 0;
3844   }
3845   else if (element == EL_DRAGON)
3846   {
3847     boolean can_turn_left  = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3848     boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3849     boolean can_move_on    = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3850     int rnd_value = 24;
3851     int rnd = RND(rnd_value);
3852
3853 #if 0
3854     if (FrameCounter < 1 && x == 0 && y == 29)
3855       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3856 #endif
3857
3858     if (can_move_on && rnd > rnd_value / 8)
3859       MovDir[x][y] = old_move_dir;
3860     else if (can_turn_left && can_turn_right)
3861       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3862     else if (can_turn_left && rnd > rnd_value / 8)
3863       MovDir[x][y] = left_dir;
3864     else if (can_turn_right && rnd > rnd_value / 8)
3865       MovDir[x][y] = right_dir;
3866     else
3867       MovDir[x][y] = back_dir;
3868
3869     xx = x + move_xy[MovDir[x][y]].x;
3870     yy = y + move_xy[MovDir[x][y]].y;
3871
3872 #if 0
3873     if (FrameCounter < 1 && x == 0 && y == 29)
3874       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3875              xx, yy, Feld[xx][yy],
3876              FrameCounter);
3877 #endif
3878
3879 #if 1
3880     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3881       MovDir[x][y] = old_move_dir;
3882 #else
3883     if (!IS_FREE(xx, yy))
3884       MovDir[x][y] = old_move_dir;
3885 #endif
3886
3887 #if 0
3888     if (FrameCounter < 1 && x == 0 && y == 29)
3889       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3890 #endif
3891
3892     MovDelay[x][y] = 0;
3893   }
3894   else if (element == EL_MOLE)
3895   {
3896     boolean can_move_on =
3897       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3898                             IS_AMOEBOID(Feld[move_x][move_y]) ||
3899                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3900     if (!can_move_on)
3901     {
3902       boolean can_turn_left =
3903         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3904                               IS_AMOEBOID(Feld[left_x][left_y])));
3905
3906       boolean can_turn_right =
3907         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3908                               IS_AMOEBOID(Feld[right_x][right_y])));
3909
3910       if (can_turn_left && can_turn_right)
3911         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3912       else if (can_turn_left)
3913         MovDir[x][y] = left_dir;
3914       else
3915         MovDir[x][y] = right_dir;
3916     }
3917
3918     if (MovDir[x][y] != old_move_dir)
3919       MovDelay[x][y] = 9;
3920   }
3921   else if (element == EL_BALLOON)
3922   {
3923     MovDir[x][y] = game.balloon_dir;
3924     MovDelay[x][y] = 0;
3925   }
3926   else if (element == EL_SPRING)
3927   {
3928     if (MovDir[x][y] & MV_HORIZONTAL &&
3929         (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3930          IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3931       MovDir[x][y] = MV_NO_MOVING;
3932
3933     MovDelay[x][y] = 0;
3934   }
3935   else if (element == EL_ROBOT ||
3936            element == EL_SATELLITE ||
3937            element == EL_PENGUIN)
3938   {
3939     int attr_x = -1, attr_y = -1;
3940
3941     if (AllPlayersGone)
3942     {
3943       attr_x = ExitX;
3944       attr_y = ExitY;
3945     }
3946     else
3947     {
3948       int i;
3949
3950       for (i = 0; i < MAX_PLAYERS; i++)
3951       {
3952         struct PlayerInfo *player = &stored_player[i];
3953         int jx = player->jx, jy = player->jy;
3954
3955         if (!player->active)
3956           continue;
3957
3958         if (attr_x == -1 ||
3959             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3960         {
3961           attr_x = jx;
3962           attr_y = jy;
3963         }
3964       }
3965     }
3966
3967     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3968     {
3969       attr_x = ZX;
3970       attr_y = ZY;
3971     }
3972
3973     if (element == EL_PENGUIN)
3974     {
3975       int i;
3976       static int xy[4][2] =
3977       {
3978         { 0, -1 },
3979         { -1, 0 },
3980         { +1, 0 },
3981         { 0, +1 }
3982       };
3983
3984       for (i = 0; i < 4; i++)
3985       {
3986         int ex = x + xy[i % 4][0];
3987         int ey = y + xy[i % 4][1];
3988
3989         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3990         {
3991           attr_x = ex;
3992           attr_y = ey;
3993           break;
3994         }
3995       }
3996     }
3997
3998     MovDir[x][y] = MV_NO_MOVING;
3999     if (attr_x < x)
4000       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4001     else if (attr_x > x)
4002       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4003     if (attr_y < y)
4004       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4005     else if (attr_y > y)
4006       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4007
4008     if (element == EL_ROBOT)
4009     {
4010       int newx, newy;
4011
4012       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4013         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4014       Moving2Blocked(x, y, &newx, &newy);
4015
4016       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4017         MovDelay[x][y] = 8 + 8 * !RND(3);
4018       else
4019         MovDelay[x][y] = 16;
4020     }
4021     else if (element == EL_PENGUIN)
4022     {
4023       int newx, newy;
4024
4025       MovDelay[x][y] = 1;
4026
4027       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4028       {
4029         boolean first_horiz = RND(2);
4030         int new_move_dir = MovDir[x][y];
4031
4032         MovDir[x][y] =
4033           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4034         Moving2Blocked(x, y, &newx, &newy);
4035
4036         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4037           return;
4038
4039         MovDir[x][y] =
4040           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4041         Moving2Blocked(x, y, &newx, &newy);
4042
4043         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4044           return;
4045
4046         MovDir[x][y] = old_move_dir;
4047         return;
4048       }
4049     }
4050     else        /* (element == EL_SATELLITE) */
4051     {
4052       int newx, newy;
4053
4054       MovDelay[x][y] = 1;
4055
4056       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4057       {
4058         boolean first_horiz = RND(2);
4059         int new_move_dir = MovDir[x][y];
4060
4061         MovDir[x][y] =
4062           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4063         Moving2Blocked(x, y, &newx, &newy);
4064
4065         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4066           return;
4067
4068         MovDir[x][y] =
4069           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4070         Moving2Blocked(x, y, &newx, &newy);
4071
4072         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4073           return;
4074
4075         MovDir[x][y] = old_move_dir;
4076         return;
4077       }
4078     }
4079   }
4080   else if (move_pattern == MV_TURNING_LEFT ||
4081            move_pattern == MV_TURNING_RIGHT ||
4082            move_pattern == MV_TURNING_LEFT_RIGHT ||
4083            move_pattern == MV_TURNING_RIGHT_LEFT ||
4084            move_pattern == MV_TURNING_RANDOM ||
4085            move_pattern == MV_ALL_DIRECTIONS)
4086   {
4087     boolean can_turn_left =
4088       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4089     boolean can_turn_right =
4090       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4091
4092     if (move_pattern == MV_TURNING_LEFT)
4093       MovDir[x][y] = left_dir;
4094     else if (move_pattern == MV_TURNING_RIGHT)
4095       MovDir[x][y] = right_dir;
4096     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4097       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4098     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4099       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4100     else if (move_pattern == MV_TURNING_RANDOM)
4101       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4102                       can_turn_right && !can_turn_left ? right_dir :
4103                       RND(2) ? left_dir : right_dir);
4104     else if (can_turn_left && can_turn_right)
4105       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4106     else if (can_turn_left)
4107       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4108     else if (can_turn_right)
4109       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4110     else
4111       MovDir[x][y] = back_dir;
4112
4113     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4114   }
4115   else if (move_pattern == MV_HORIZONTAL ||
4116            move_pattern == MV_VERTICAL)
4117   {
4118     if (move_pattern & old_move_dir)
4119       MovDir[x][y] = back_dir;
4120     else if (move_pattern == MV_HORIZONTAL)
4121       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4122     else if (move_pattern == MV_VERTICAL)
4123       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4124
4125     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4126   }
4127   else if (move_pattern & MV_ANY_DIRECTION)
4128   {
4129     MovDir[x][y] = move_pattern;
4130     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4131   }
4132   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4133   {
4134     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4135       MovDir[x][y] = left_dir;
4136     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4137       MovDir[x][y] = right_dir;
4138
4139     if (MovDir[x][y] != old_move_dir)
4140       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4141   }
4142   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4143   {
4144     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4145       MovDir[x][y] = right_dir;
4146     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4147       MovDir[x][y] = left_dir;
4148
4149     if (MovDir[x][y] != old_move_dir)
4150       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4151   }
4152   else if (move_pattern == MV_TOWARDS_PLAYER ||
4153            move_pattern == MV_AWAY_FROM_PLAYER)
4154   {
4155     int attr_x = -1, attr_y = -1;
4156     int newx, newy;
4157     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4158
4159     if (AllPlayersGone)
4160     {
4161       attr_x = ExitX;
4162       attr_y = ExitY;
4163     }
4164     else
4165     {
4166       int i;
4167
4168       for (i = 0; i < MAX_PLAYERS; i++)
4169       {
4170         struct PlayerInfo *player = &stored_player[i];
4171         int jx = player->jx, jy = player->jy;
4172
4173         if (!player->active)
4174           continue;
4175
4176         if (attr_x == -1 ||
4177             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4178         {
4179           attr_x = jx;
4180           attr_y = jy;
4181         }
4182       }
4183     }
4184
4185     MovDir[x][y] = MV_NO_MOVING;
4186     if (attr_x < x)
4187       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4188     else if (attr_x > x)
4189       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4190     if (attr_y < y)
4191       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4192     else if (attr_y > y)
4193       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4194
4195     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4196
4197     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4198     {
4199       boolean first_horiz = RND(2);
4200       int new_move_dir = MovDir[x][y];
4201
4202       MovDir[x][y] =
4203         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4204       Moving2Blocked(x, y, &newx, &newy);
4205
4206       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4207         return;
4208
4209       MovDir[x][y] =
4210         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4211       Moving2Blocked(x, y, &newx, &newy);
4212
4213       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4214         return;
4215
4216       MovDir[x][y] = old_move_dir;
4217     }
4218   }
4219   else if (move_pattern == MV_WHEN_PUSHED ||
4220            move_pattern == MV_WHEN_DROPPED)
4221   {
4222     if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4223       MovDir[x][y] = MV_NO_MOVING;
4224
4225     MovDelay[x][y] = 0;
4226   }
4227   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4228   {
4229     static int test_xy[7][2] =
4230     {
4231       { 0, -1 },
4232       { -1, 0 },
4233       { +1, 0 },
4234       { 0, +1 },
4235       { 0, -1 },
4236       { -1, 0 },
4237       { +1, 0 },
4238     };
4239     static int test_dir[7] =
4240     {
4241       MV_UP,
4242       MV_LEFT,
4243       MV_RIGHT,
4244       MV_DOWN,
4245       MV_UP,
4246       MV_LEFT,
4247       MV_RIGHT,
4248     };
4249     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4250     int move_preference = -1000000;     /* start with very low preference */
4251     int new_move_dir = MV_NO_MOVING;
4252     int start_test = RND(4);
4253     int i;
4254
4255     for (i = 0; i < 4; i++)
4256     {
4257       int move_dir = test_dir[start_test + i];
4258       int move_dir_preference;
4259
4260       xx = x + test_xy[start_test + i][0];
4261       yy = y + test_xy[start_test + i][1];
4262
4263       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4264           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4265       {
4266         new_move_dir = move_dir;
4267
4268         break;
4269       }
4270
4271       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4272         continue;
4273
4274       move_dir_preference = -1 * RunnerVisit[xx][yy];
4275       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4276         move_dir_preference = PlayerVisit[xx][yy];
4277
4278       if (move_dir_preference > move_preference)
4279       {
4280         /* prefer field that has not been visited for the longest time */
4281         move_preference = move_dir_preference;
4282         new_move_dir = move_dir;
4283       }
4284       else if (move_dir_preference == move_preference &&
4285                move_dir == old_move_dir)
4286       {
4287         /* prefer last direction when all directions are preferred equally */
4288         move_preference = move_dir_preference;
4289         new_move_dir = move_dir;
4290       }
4291     }
4292
4293     MovDir[x][y] = new_move_dir;
4294     if (old_move_dir != new_move_dir)
4295       MovDelay[x][y] = 9;
4296   }
4297 }
4298
4299 static void TurnRound(int x, int y)
4300 {
4301   int direction = MovDir[x][y];
4302
4303 #if 0
4304   GfxDir[x][y] = MovDir[x][y];
4305 #endif
4306
4307   TurnRoundExt(x, y);
4308
4309 #if 1
4310   GfxDir[x][y] = MovDir[x][y];
4311 #endif
4312
4313   if (direction != MovDir[x][y])
4314     GfxFrame[x][y] = 0;
4315
4316 #if 1
4317   if (MovDelay[x][y])
4318     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4319 #else
4320   if (MovDelay[x][y])
4321     GfxAction[x][y] = ACTION_WAITING;
4322 #endif
4323 }
4324
4325 static boolean JustBeingPushed(int x, int y)
4326 {
4327   int i;
4328
4329   for (i = 0; i < MAX_PLAYERS; i++)
4330   {
4331     struct PlayerInfo *player = &stored_player[i];
4332
4333     if (player->active && player->is_pushing && player->MovPos)
4334     {
4335       int next_jx = player->jx + (player->jx - player->last_jx);
4336       int next_jy = player->jy + (player->jy - player->last_jy);
4337
4338       if (x == next_jx && y == next_jy)
4339         return TRUE;
4340     }
4341   }
4342
4343   return FALSE;
4344 }
4345
4346 void StartMoving(int x, int y)
4347 {
4348   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4349   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4350   int element = Feld[x][y];
4351
4352   if (Stop[x][y])
4353     return;
4354
4355 #if 1
4356   if (MovDelay[x][y] == 0)
4357     GfxAction[x][y] = ACTION_DEFAULT;
4358 #else
4359   /* !!! this should be handled more generic (not only for mole) !!! */
4360   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4361     GfxAction[x][y] = ACTION_DEFAULT;
4362 #endif
4363
4364   if (CAN_FALL(element) && y < lev_fieldy - 1)
4365   {
4366     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
4367         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4368       if (JustBeingPushed(x, y))
4369         return;
4370
4371     if (element == EL_QUICKSAND_FULL)
4372     {
4373       if (IS_FREE(x, y + 1))
4374       {
4375         InitMovingField(x, y, MV_DOWN);
4376         started_moving = TRUE;
4377
4378         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4379         Store[x][y] = EL_ROCK;
4380 #if 1
4381         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4382 #else
4383         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4384 #endif
4385       }
4386       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4387       {
4388         if (!MovDelay[x][y])
4389           MovDelay[x][y] = TILEY + 1;
4390
4391         if (MovDelay[x][y])
4392         {
4393           MovDelay[x][y]--;
4394           if (MovDelay[x][y])
4395             return;
4396         }
4397
4398         Feld[x][y] = EL_QUICKSAND_EMPTY;
4399         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4400         Store[x][y + 1] = Store[x][y];
4401         Store[x][y] = 0;
4402 #if 1
4403         PlayLevelSoundAction(x, y, ACTION_FILLING);
4404 #else
4405         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4406 #endif
4407       }
4408     }
4409     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4410              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4411     {
4412       InitMovingField(x, y, MV_DOWN);
4413       started_moving = TRUE;
4414
4415       Feld[x][y] = EL_QUICKSAND_FILLING;
4416       Store[x][y] = element;
4417 #if 1
4418       PlayLevelSoundAction(x, y, ACTION_FILLING);
4419 #else
4420       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4421 #endif
4422     }
4423     else if (element == EL_MAGIC_WALL_FULL)
4424     {
4425       if (IS_FREE(x, y + 1))
4426       {
4427         InitMovingField(x, y, MV_DOWN);
4428         started_moving = TRUE;
4429
4430         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4431         Store[x][y] = EL_CHANGED(Store[x][y]);
4432       }
4433       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4434       {
4435         if (!MovDelay[x][y])
4436           MovDelay[x][y] = TILEY/4 + 1;
4437
4438         if (MovDelay[x][y])
4439         {
4440           MovDelay[x][y]--;
4441           if (MovDelay[x][y])
4442             return;
4443         }
4444
4445         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4446         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4447         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4448         Store[x][y] = 0;
4449       }
4450     }
4451     else if (element == EL_BD_MAGIC_WALL_FULL)
4452     {
4453       if (IS_FREE(x, y + 1))
4454       {
4455         InitMovingField(x, y, MV_DOWN);
4456         started_moving = TRUE;
4457
4458         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4459         Store[x][y] = EL_CHANGED2(Store[x][y]);
4460       }
4461       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4462       {
4463         if (!MovDelay[x][y])
4464           MovDelay[x][y] = TILEY/4 + 1;
4465
4466         if (MovDelay[x][y])
4467         {
4468           MovDelay[x][y]--;
4469           if (MovDelay[x][y])
4470             return;
4471         }
4472
4473         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4474         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4475         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4476         Store[x][y] = 0;
4477       }
4478     }
4479     else if (CAN_PASS_MAGIC_WALL(element) &&
4480              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4481               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4482     {
4483       InitMovingField(x, y, MV_DOWN);
4484       started_moving = TRUE;
4485
4486       Feld[x][y] =
4487         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4488          EL_BD_MAGIC_WALL_FILLING);
4489       Store[x][y] = element;
4490     }
4491 #if 0
4492     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4493 #else
4494     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4495 #endif
4496     {
4497       SplashAcid(x, y);
4498
4499       InitMovingField(x, y, MV_DOWN);
4500       started_moving = TRUE;
4501
4502       Store[x][y] = EL_ACID;
4503 #if 0
4504       /* !!! TEST !!! better use "_FALLING" etc. !!! */
4505       GfxAction[x][y + 1] = ACTION_ACTIVE;
4506 #endif
4507     }
4508 #if 1
4509     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4510               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4511               (Feld[x][y + 1] == EL_BLOCKED)) ||
4512              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4513               CAN_SMASH(element) && WasJustFalling[x][y] &&
4514               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4515
4516 #else
4517 #if 1
4518     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4519              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4520              WasJustMoving[x][y] && !Pushed[x][y + 1])
4521 #else
4522     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4523              WasJustMoving[x][y])
4524 #endif
4525 #endif
4526
4527     {
4528       /* this is needed for a special case not covered by calling "Impact()"
4529          from "ContinueMoving()": if an element moves to a tile directly below
4530          another element which was just falling on that tile (which was empty
4531          in the previous frame), the falling element above would just stop
4532          instead of smashing the element below (in previous version, the above
4533          element was just checked for "moving" instead of "falling", resulting
4534          in incorrect smashes caused by horizontal movement of the above
4535          element; also, the case of the player being the element to smash was
4536          simply not covered here... :-/ ) */
4537
4538 #if 0
4539       WasJustMoving[x][y] = 0;
4540       WasJustFalling[x][y] = 0;
4541 #endif
4542
4543       Impact(x, y);
4544     }
4545     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4546     {
4547       if (MovDir[x][y] == MV_NO_MOVING)
4548       {
4549         InitMovingField(x, y, MV_DOWN);
4550         started_moving = TRUE;
4551       }
4552     }
4553     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4554     {
4555       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4556         MovDir[x][y] = MV_DOWN;
4557
4558       InitMovingField(x, y, MV_DOWN);
4559       started_moving = TRUE;
4560     }
4561     else if (element == EL_AMOEBA_DROP)
4562     {
4563       Feld[x][y] = EL_AMOEBA_GROWING;
4564       Store[x][y] = EL_AMOEBA_WET;
4565     }
4566     /* Store[x][y + 1] must be zero, because:
4567        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4568     */
4569 #if 0
4570 #if OLD_GAME_BEHAVIOUR
4571     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4572 #else
4573     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4574              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4575              element != EL_DX_SUPABOMB)
4576 #endif
4577 #else
4578     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4579               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4580              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4581              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4582 #endif
4583     {
4584       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4585                                 (IS_FREE(x - 1, y + 1) ||
4586                                  Feld[x - 1][y + 1] == EL_ACID));
4587       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4588                                 (IS_FREE(x + 1, y + 1) ||
4589                                  Feld[x + 1][y + 1] == EL_ACID));
4590       boolean can_fall_any  = (can_fall_left || can_fall_right);
4591       boolean can_fall_both = (can_fall_left && can_fall_right);
4592
4593       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4594       {
4595         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4596
4597         if (slippery_type == SLIPPERY_ONLY_LEFT)
4598           can_fall_right = FALSE;
4599         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4600           can_fall_left = FALSE;
4601         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4602           can_fall_right = FALSE;
4603         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4604           can_fall_left = FALSE;
4605
4606         can_fall_any  = (can_fall_left || can_fall_right);
4607         can_fall_both = (can_fall_left && can_fall_right);
4608       }
4609
4610       if (can_fall_any)
4611       {
4612         if (can_fall_both &&
4613             (game.emulation != EMU_BOULDERDASH &&
4614              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4615           can_fall_left = !(can_fall_right = RND(2));
4616
4617         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4618         started_moving = TRUE;
4619       }
4620     }
4621 #if 1
4622     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4623 #else
4624     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4625 #endif
4626     {
4627       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4628       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4629       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4630       int belt_dir = game.belt_dir[belt_nr];
4631
4632       if ((belt_dir == MV_LEFT  && left_is_free) ||
4633           (belt_dir == MV_RIGHT && right_is_free))
4634       {
4635         InitMovingField(x, y, belt_dir);
4636         started_moving = TRUE;
4637
4638         GfxAction[x][y] = ACTION_DEFAULT;
4639       }
4640     }
4641   }
4642
4643   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4644   if (CAN_MOVE(element) && !started_moving)
4645   {
4646     int move_pattern = element_info[element].move_pattern;
4647     int newx, newy;
4648
4649     Moving2Blocked(x, y, &newx, &newy);
4650
4651 #if 1
4652     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4653       return;
4654 #else
4655     if ((element == EL_SATELLITE ||
4656          element == EL_BALLOON ||
4657          element == EL_SPRING)
4658         && JustBeingPushed(x, y))
4659       return;
4660 #endif
4661
4662 #if 1
4663     if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4664         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4665         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4666     {
4667 #if 0
4668       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4669              element, element_info[element].token_name,
4670              WasJustMoving[x][y],
4671              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4672              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4673              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4674              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4675 #endif
4676
4677 #if 1
4678       WasJustMoving[x][y] = 0;
4679 #endif
4680
4681       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4682
4683 #if 0
4684       if (Feld[x][y] != element)        /* element has changed */
4685       {
4686         element = Feld[x][y];
4687         move_pattern = element_info[element].move_pattern;
4688
4689         if (!CAN_MOVE(element))
4690           return;
4691       }
4692 #else
4693       if (Feld[x][y] != element)        /* element has changed */
4694         return;
4695 #endif
4696     }
4697 #endif
4698
4699 #if 0
4700 #if 0
4701     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4702       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4703 #else
4704     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4705     {
4706       Moving2Blocked(x, y, &newx, &newy);
4707       if (Feld[newx][newy] == EL_BLOCKED)
4708         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4709     }
4710 #endif
4711 #endif
4712
4713 #if 0
4714     if (FrameCounter < 1 && x == 0 && y == 29)
4715       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4716 #endif
4717
4718     if (!MovDelay[x][y])        /* start new movement phase */
4719     {
4720       /* all objects that can change their move direction after each step
4721          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4722
4723       if (element != EL_YAMYAM &&
4724           element != EL_DARK_YAMYAM &&
4725           element != EL_PACMAN &&
4726           !(move_pattern & MV_ANY_DIRECTION) &&
4727           move_pattern != MV_TURNING_LEFT &&
4728           move_pattern != MV_TURNING_RIGHT &&
4729           move_pattern != MV_TURNING_LEFT_RIGHT &&
4730           move_pattern != MV_TURNING_RIGHT_LEFT &&
4731           move_pattern != MV_TURNING_RANDOM)
4732       {
4733         TurnRound(x, y);
4734
4735 #if 0
4736         if (FrameCounter < 1 && x == 0 && y == 29)
4737           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4738 #endif
4739
4740         if (MovDelay[x][y] && (element == EL_BUG ||
4741                                element == EL_SPACESHIP ||
4742                                element == EL_SP_SNIKSNAK ||
4743                                element == EL_SP_ELECTRON ||
4744                                element == EL_MOLE))
4745           DrawLevelField(x, y);
4746       }
4747     }
4748
4749     if (MovDelay[x][y])         /* wait some time before next movement */
4750     {
4751       MovDelay[x][y]--;
4752
4753 #if 0
4754       if (element == EL_YAMYAM)
4755       {
4756         printf("::: %d\n",
4757                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4758         DrawLevelElementAnimation(x, y, element);
4759       }
4760 #endif
4761
4762       if (MovDelay[x][y])       /* element still has to wait some time */
4763       {
4764 #if 0
4765         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4766         ResetGfxAnimation(x, y);
4767 #endif
4768
4769 #if 0
4770         if (GfxAction[x][y] != ACTION_WAITING)
4771           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4772
4773         GfxAction[x][y] = ACTION_WAITING;
4774 #endif
4775       }
4776
4777       if (element == EL_ROBOT ||
4778 #if 0
4779           element == EL_PACMAN ||
4780 #endif
4781           element == EL_YAMYAM ||
4782           element == EL_DARK_YAMYAM)
4783       {
4784 #if 0
4785         DrawLevelElementAnimation(x, y, element);
4786 #else
4787         DrawLevelElementAnimationIfNeeded(x, y, element);
4788 #endif
4789         PlayLevelSoundAction(x, y, ACTION_WAITING);
4790       }
4791       else if (element == EL_SP_ELECTRON)
4792         DrawLevelElementAnimationIfNeeded(x, y, element);
4793       else if (element == EL_DRAGON)
4794       {
4795         int i;
4796         int dir = MovDir[x][y];
4797         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4798         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4799         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4800                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4801                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4802                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4803         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4804
4805 #if 0
4806         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4807 #endif
4808
4809         GfxAction[x][y] = ACTION_ATTACKING;
4810
4811         if (IS_PLAYER(x, y))
4812           DrawPlayerField(x, y);
4813         else
4814           DrawLevelField(x, y);
4815
4816         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4817
4818         for (i = 1; i <= 3; i++)
4819         {
4820           int xx = x + i * dx;
4821           int yy = y + i * dy;
4822           int sx = SCREENX(xx);
4823           int sy = SCREENY(yy);
4824           int flame_graphic = graphic + (i - 1);
4825
4826           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4827             break;
4828
4829           if (MovDelay[x][y])
4830           {
4831             int flamed = MovingOrBlocked2Element(xx, yy);
4832
4833             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
4834               Bang(xx, yy);
4835             else
4836               RemoveMovingField(xx, yy);
4837
4838             Feld[xx][yy] = EL_FLAMES;
4839             if (IN_SCR_FIELD(sx, sy))
4840             {
4841               DrawLevelFieldCrumbledSand(xx, yy);
4842               DrawGraphic(sx, sy, flame_graphic, frame);
4843             }
4844           }
4845           else
4846           {
4847             if (Feld[xx][yy] == EL_FLAMES)
4848               Feld[xx][yy] = EL_EMPTY;
4849             DrawLevelField(xx, yy);
4850           }
4851         }
4852       }
4853
4854       if (MovDelay[x][y])       /* element still has to wait some time */
4855       {
4856         PlayLevelSoundAction(x, y, ACTION_WAITING);
4857
4858         return;
4859       }
4860
4861 #if 0
4862       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4863          for all other elements GfxAction will be set by InitMovingField() */
4864       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4865         GfxAction[x][y] = ACTION_MOVING;
4866 #endif
4867     }
4868
4869     /* now make next step */
4870
4871     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4872
4873     if (DONT_COLLIDE_WITH(element) &&
4874         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4875         !PLAYER_ENEMY_PROTECTED(newx, newy))
4876     {
4877 #if 1
4878       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4879
4880       return;
4881 #else
4882       /* player killed by element which is deadly when colliding with */
4883       MovDir[x][y] = 0;
4884       KillHero(PLAYERINFO(newx, newy));
4885       return;
4886 #endif
4887
4888     }
4889     else if ((element == EL_PENGUIN ||
4890               element == EL_ROBOT ||
4891               element == EL_SATELLITE ||
4892               element == EL_BALLOON ||
4893               IS_CUSTOM_ELEMENT(element)) &&
4894              IN_LEV_FIELD(newx, newy) &&
4895              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4896     {
4897       SplashAcid(x, y);
4898       Store[x][y] = EL_ACID;
4899     }
4900     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4901     {
4902       if (Feld[newx][newy] == EL_EXIT_OPEN)
4903       {
4904 #if 1
4905         RemoveField(x, y);
4906         DrawLevelField(x, y);
4907 #else
4908         Feld[x][y] = EL_EMPTY;
4909         DrawLevelField(x, y);
4910 #endif
4911
4912         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4913         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4914           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4915
4916         local_player->friends_still_needed--;
4917         if (!local_player->friends_still_needed &&
4918             !local_player->GameOver && AllPlayersGone)
4919           local_player->LevelSolved = local_player->GameOver = TRUE;
4920
4921         return;
4922       }
4923       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4924       {
4925         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4926           DrawLevelField(newx, newy);
4927         else
4928           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4929       }
4930       else if (!IS_FREE(newx, newy))
4931       {
4932         GfxAction[x][y] = ACTION_WAITING;
4933
4934         if (IS_PLAYER(x, y))
4935           DrawPlayerField(x, y);
4936         else
4937           DrawLevelField(x, y);
4938
4939         return;
4940       }
4941     }
4942     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4943     {
4944       if (IS_FOOD_PIG(Feld[newx][newy]))
4945       {
4946         if (IS_MOVING(newx, newy))
4947           RemoveMovingField(newx, newy);
4948         else
4949         {
4950           Feld[newx][newy] = EL_EMPTY;
4951           DrawLevelField(newx, newy);
4952         }
4953
4954         PlayLevelSound(x, y, SND_PIG_DIGGING);
4955       }
4956       else if (!IS_FREE(newx, newy))
4957       {
4958         if (IS_PLAYER(x, y))
4959           DrawPlayerField(x, y);
4960         else
4961           DrawLevelField(x, y);
4962
4963         return;
4964       }
4965     }
4966
4967 #if 1
4968
4969     /*
4970     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4971     */
4972
4973     else if (IS_CUSTOM_ELEMENT(element) &&
4974              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4975
4976 #if 0
4977  &&
4978              !IS_FREE(newx, newy)
4979 #endif
4980
4981 )
4982     {
4983       int new_element = Feld[newx][newy];
4984
4985 #if 0
4986       printf("::: '%s' digs '%s' [%d]\n",
4987              element_info[element].token_name,
4988              element_info[Feld[newx][newy]].token_name,
4989              StorePlayer[newx][newy]);
4990 #endif
4991
4992       if (!IS_FREE(newx, newy))
4993       {
4994         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4995                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4996                       ACTION_BREAKING);
4997
4998         /* no element can dig solid indestructible elements */
4999         if (IS_INDESTRUCTIBLE(new_element) &&
5000             !IS_DIGGABLE(new_element) &&
5001             !IS_COLLECTIBLE(new_element))
5002           return;
5003
5004         if (AmoebaNr[newx][newy] &&
5005             (new_element == EL_AMOEBA_FULL ||
5006              new_element == EL_BD_AMOEBA ||
5007              new_element == EL_AMOEBA_GROWING))
5008         {
5009           AmoebaCnt[AmoebaNr[newx][newy]]--;
5010           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5011         }
5012
5013         if (IS_MOVING(newx, newy))
5014           RemoveMovingField(newx, newy);
5015         else
5016         {
5017           RemoveField(newx, newy);
5018           DrawLevelField(newx, newy);
5019         }
5020
5021         PlayLevelSoundAction(x, y, action);
5022       }
5023
5024       if (new_element == element_info[element].move_enter_element)
5025         element_info[element].can_leave_element = TRUE;
5026
5027       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5028       {
5029         RunnerVisit[x][y] = FrameCounter;
5030         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5031       }
5032     }
5033
5034 #endif
5035
5036     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5037     {
5038       if (!IS_FREE(newx, newy))
5039       {
5040         if (IS_PLAYER(x, y))
5041           DrawPlayerField(x, y);
5042         else
5043           DrawLevelField(x, y);
5044
5045         return;
5046       }
5047       else
5048       {
5049         boolean wanna_flame = !RND(10);
5050         int dx = newx - x, dy = newy - y;
5051         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5052         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5053         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5054                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5055         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5056                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5057
5058         if ((wanna_flame ||
5059              IS_CLASSIC_ENEMY(element1) ||
5060              IS_CLASSIC_ENEMY(element2)) &&
5061             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5062             element1 != EL_FLAMES && element2 != EL_FLAMES)
5063         {
5064 #if 1
5065           ResetGfxAnimation(x, y);
5066           GfxAction[x][y] = ACTION_ATTACKING;
5067 #endif
5068
5069           if (IS_PLAYER(x, y))
5070             DrawPlayerField(x, y);
5071           else
5072             DrawLevelField(x, y);
5073
5074           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5075
5076           MovDelay[x][y] = 50;
5077
5078           Feld[newx][newy] = EL_FLAMES;
5079           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5080             Feld[newx1][newy1] = EL_FLAMES;
5081           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5082             Feld[newx2][newy2] = EL_FLAMES;
5083
5084           return;
5085         }
5086       }
5087     }
5088     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5089              Feld[newx][newy] == EL_DIAMOND)
5090     {
5091       if (IS_MOVING(newx, newy))
5092         RemoveMovingField(newx, newy);
5093       else
5094       {
5095         Feld[newx][newy] = EL_EMPTY;
5096         DrawLevelField(newx, newy);
5097       }
5098
5099       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5100     }
5101     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5102              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5103     {
5104       if (AmoebaNr[newx][newy])
5105       {
5106         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5107         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5108             Feld[newx][newy] == EL_BD_AMOEBA)
5109           AmoebaCnt[AmoebaNr[newx][newy]]--;
5110       }
5111
5112       if (IS_MOVING(newx, newy))
5113         RemoveMovingField(newx, newy);
5114       else
5115       {
5116         Feld[newx][newy] = EL_EMPTY;
5117         DrawLevelField(newx, newy);
5118       }
5119
5120       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5121     }
5122     else if ((element == EL_PACMAN || element == EL_MOLE)
5123              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5124     {
5125       if (AmoebaNr[newx][newy])
5126       {
5127         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5128         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5129             Feld[newx][newy] == EL_BD_AMOEBA)
5130           AmoebaCnt[AmoebaNr[newx][newy]]--;
5131       }
5132
5133       if (element == EL_MOLE)
5134       {
5135         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5136         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5137
5138         ResetGfxAnimation(x, y);
5139         GfxAction[x][y] = ACTION_DIGGING;
5140         DrawLevelField(x, y);
5141
5142         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5143
5144         return;                         /* wait for shrinking amoeba */
5145       }
5146       else      /* element == EL_PACMAN */
5147       {
5148         Feld[newx][newy] = EL_EMPTY;
5149         DrawLevelField(newx, newy);
5150         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5151       }
5152     }
5153     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5154              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5155               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5156     {
5157       /* wait for shrinking amoeba to completely disappear */
5158       return;
5159     }
5160     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5161     {
5162       /* object was running against a wall */
5163
5164       TurnRound(x, y);
5165
5166 #if 1
5167       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5168         DrawLevelElementAnimation(x, y, element);
5169 #else
5170       if (element == EL_BUG ||
5171           element == EL_SPACESHIP ||
5172           element == EL_SP_SNIKSNAK)
5173         DrawLevelField(x, y);
5174       else if (element == EL_MOLE)
5175         DrawLevelField(x, y);
5176       else if (element == EL_BD_BUTTERFLY ||
5177                element == EL_BD_FIREFLY)
5178         DrawLevelElementAnimationIfNeeded(x, y, element);
5179       else if (element == EL_SATELLITE)
5180         DrawLevelElementAnimationIfNeeded(x, y, element);
5181       else if (element == EL_SP_ELECTRON)
5182         DrawLevelElementAnimationIfNeeded(x, y, element);
5183 #endif
5184
5185       if (DONT_TOUCH(element))
5186         TestIfBadThingTouchesHero(x, y);
5187
5188 #if 0
5189       PlayLevelSoundAction(x, y, ACTION_WAITING);
5190 #endif
5191
5192       return;
5193     }
5194
5195     InitMovingField(x, y, MovDir[x][y]);
5196
5197     PlayLevelSoundAction(x, y, ACTION_MOVING);
5198   }
5199
5200   if (MovDir[x][y])
5201     ContinueMoving(x, y);
5202 }
5203
5204 void ContinueMoving(int x, int y)
5205 {
5206   int element = Feld[x][y];
5207   struct ElementInfo *ei = &element_info[element];
5208   int direction = MovDir[x][y];
5209   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5210   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5211   int newx = x + dx, newy = y + dy;
5212 #if 0
5213   int nextx = newx + dx, nexty = newy + dy;
5214 #endif
5215   boolean pushed = Pushed[x][y];
5216
5217   MovPos[x][y] += getElementMoveStepsize(x, y);
5218
5219   if (pushed)           /* special case: moving object pushed by player */
5220     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5221
5222   if (ABS(MovPos[x][y]) < TILEX)
5223   {
5224     DrawLevelField(x, y);
5225
5226     return;     /* element is still moving */
5227   }
5228
5229   /* element reached destination field */
5230
5231   Feld[x][y] = EL_EMPTY;
5232   Feld[newx][newy] = element;
5233   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5234
5235   if (element == EL_MOLE)
5236   {
5237     Feld[x][y] = EL_SAND;
5238
5239     DrawLevelFieldCrumbledSandNeighbours(x, y);
5240   }
5241   else if (element == EL_QUICKSAND_FILLING)
5242   {
5243     element = Feld[newx][newy] = get_next_element(element);
5244     Store[newx][newy] = Store[x][y];
5245   }
5246   else if (element == EL_QUICKSAND_EMPTYING)
5247   {
5248     Feld[x][y] = get_next_element(element);
5249     element = Feld[newx][newy] = Store[x][y];
5250   }
5251   else if (element == EL_MAGIC_WALL_FILLING)
5252   {
5253     element = Feld[newx][newy] = get_next_element(element);
5254     if (!game.magic_wall_active)
5255       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5256     Store[newx][newy] = Store[x][y];
5257   }
5258   else if (element == EL_MAGIC_WALL_EMPTYING)
5259   {
5260     Feld[x][y] = get_next_element(element);
5261     if (!game.magic_wall_active)
5262       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5263     element = Feld[newx][newy] = Store[x][y];
5264   }
5265   else if (element == EL_BD_MAGIC_WALL_FILLING)
5266   {
5267     element = Feld[newx][newy] = get_next_element(element);
5268     if (!game.magic_wall_active)
5269       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5270     Store[newx][newy] = Store[x][y];
5271   }
5272   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5273   {
5274     Feld[x][y] = get_next_element(element);
5275     if (!game.magic_wall_active)
5276       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5277     element = Feld[newx][newy] = Store[x][y];
5278   }
5279   else if (element == EL_AMOEBA_DROPPING)
5280   {
5281     Feld[x][y] = get_next_element(element);
5282     element = Feld[newx][newy] = Store[x][y];
5283   }
5284   else if (element == EL_SOKOBAN_OBJECT)
5285   {
5286     if (Back[x][y])
5287       Feld[x][y] = Back[x][y];
5288
5289     if (Back[newx][newy])
5290       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5291
5292     Back[x][y] = Back[newx][newy] = 0;
5293   }
5294   else if (Store[x][y] == EL_ACID)
5295   {
5296     element = Feld[newx][newy] = EL_ACID;
5297   }
5298
5299   Store[x][y] = 0;
5300   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5301   MovDelay[newx][newy] = 0;
5302
5303   /* copy element change control values to new field */
5304   ChangeDelay[newx][newy] = ChangeDelay[x][y];
5305   ChangePage[newx][newy] = ChangePage[x][y];
5306   Changed[newx][newy] = Changed[x][y];
5307   ChangeEvent[newx][newy] = ChangeEvent[x][y];
5308
5309   ChangeDelay[x][y] = 0;
5310   ChangePage[x][y] = -1;
5311   Changed[x][y] = CE_BITMASK_DEFAULT;
5312   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5313
5314   /* copy animation control values to new field */
5315   GfxFrame[newx][newy]  = GfxFrame[x][y];
5316   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5317   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5318   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5319
5320   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5321
5322   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5323
5324 #if 1
5325   /* some elements can leave other elements behind after moving */
5326   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5327       ei->move_leave_element != EL_EMPTY &&
5328       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5329        ei->can_leave_element_last))
5330   {
5331     Feld[x][y] = ei->move_leave_element;
5332     InitField(x, y, FALSE);
5333
5334     if (GFX_CRUMBLED(Feld[x][y]))
5335       DrawLevelFieldCrumbledSandNeighbours(x, y);
5336   }
5337
5338   ei->can_leave_element_last = ei->can_leave_element;
5339   ei->can_leave_element = FALSE;
5340 #endif
5341
5342 #if 0
5343   /* 2.1.1 (does not work correctly for spring) */
5344   if (!CAN_MOVE(element))
5345     MovDir[newx][newy] = 0;
5346 #else
5347
5348 #if 0
5349   /* (does not work for falling objects that slide horizontally) */
5350   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5351     MovDir[newx][newy] = 0;
5352 #else
5353   /*
5354   if (!CAN_MOVE(element) ||
5355       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5356     MovDir[newx][newy] = 0;
5357   */
5358
5359   if (!CAN_MOVE(element) ||
5360       (CAN_FALL(element) && direction == MV_DOWN))
5361     GfxDir[x][y] = MovDir[newx][newy] = 0;
5362
5363 #endif
5364 #endif
5365
5366   DrawLevelField(x, y);
5367   DrawLevelField(newx, newy);
5368
5369   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5370
5371   /* prevent pushed element from moving on in pushed direction */
5372   if (pushed && CAN_MOVE(element) &&
5373       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5374       !(element_info[element].move_pattern & direction))
5375     TurnRound(newx, newy);
5376
5377   if (!pushed)  /* special case: moving object pushed by player */
5378   {
5379     WasJustMoving[newx][newy] = 3;
5380
5381     if (CAN_FALL(element) && direction == MV_DOWN)
5382       WasJustFalling[newx][newy] = 3;
5383   }
5384
5385   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5386   {
5387     TestIfBadThingTouchesHero(newx, newy);
5388     TestIfBadThingTouchesFriend(newx, newy);
5389
5390     if (!IS_CUSTOM_ELEMENT(element))
5391       TestIfBadThingTouchesOtherBadThing(newx, newy);
5392   }
5393   else if (element == EL_PENGUIN)
5394     TestIfFriendTouchesBadThing(newx, newy);
5395
5396   if (CAN_FALL(element) && direction == MV_DOWN &&
5397       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5398     Impact(x, newy);
5399
5400 #if 1
5401   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5402 #endif
5403
5404 #if 0
5405   if (ChangePage[newx][newy] != -1)                     /* delayed change */
5406     ChangeElement(newx, newy, ChangePage[newx][newy]);
5407 #endif
5408
5409 #if 1
5410
5411   TestIfElementHitsCustomElement(newx, newy, direction);
5412
5413 #else
5414
5415   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5416   {
5417     int hitting_element = Feld[newx][newy];
5418
5419     /* !!! fix side (direction) orientation here and elsewhere !!! */
5420     CheckElementSideChange(newx, newy, hitting_element,
5421                            direction, CE_HITTING_SOMETHING, -1);
5422
5423 #if 0
5424     if (IN_LEV_FIELD(nextx, nexty))
5425     {
5426       int opposite_direction = MV_DIR_OPPOSITE(direction);
5427       int hitting_side = direction;
5428       int touched_side = opposite_direction;
5429       int touched_element = MovingOrBlocked2Element(nextx, nexty);
5430       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5431                             MovDir[nextx][nexty] != direction ||
5432                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5433
5434       if (object_hit)
5435       {
5436         int i;
5437
5438         CheckElementSideChange(nextx, nexty, touched_element,
5439                                opposite_direction, CE_HIT_BY_SOMETHING, -1);
5440
5441         if (IS_CUSTOM_ELEMENT(hitting_element) &&
5442             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5443         {
5444           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5445           {
5446             struct ElementChangeInfo *change =
5447               &element_info[hitting_element].change_page[i];
5448
5449             if (change->can_change &&
5450                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5451                 change->sides & touched_side &&
5452                 change->trigger_element == touched_element)
5453             {
5454               CheckElementSideChange(newx, newy, hitting_element,
5455                                      CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5456               break;
5457             }
5458           }
5459         }
5460
5461         if (IS_CUSTOM_ELEMENT(touched_element) &&
5462             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5463         {
5464           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5465           {
5466             struct ElementChangeInfo *change =
5467               &element_info[touched_element].change_page[i];
5468
5469             if (change->can_change &&
5470                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5471                 change->sides & hitting_side &&
5472                 change->trigger_element == hitting_element)
5473             {
5474               CheckElementSideChange(nextx, nexty, touched_element,
5475                                      CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5476               break;
5477             }
5478           }
5479         }
5480       }
5481     }
5482 #endif
5483   }
5484 #endif
5485
5486   TestIfPlayerTouchesCustomElement(newx, newy);
5487   TestIfElementTouchesCustomElement(newx, newy);
5488 }
5489
5490 int AmoebeNachbarNr(int ax, int ay)
5491 {
5492   int i;
5493   int element = Feld[ax][ay];
5494   int group_nr = 0;
5495   static int xy[4][2] =
5496   {
5497     { 0, -1 },
5498     { -1, 0 },
5499     { +1, 0 },
5500     { 0, +1 }
5501   };
5502
5503   for (i = 0; i < 4; i++)
5504   {
5505     int x = ax + xy[i][0];
5506     int y = ay + xy[i][1];
5507
5508     if (!IN_LEV_FIELD(x, y))
5509       continue;
5510
5511     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5512       group_nr = AmoebaNr[x][y];
5513   }
5514
5515   return group_nr;
5516 }
5517
5518 void AmoebenVereinigen(int ax, int ay)
5519 {
5520   int i, x, y, xx, yy;
5521   int new_group_nr = AmoebaNr[ax][ay];
5522   static int xy[4][2] =
5523   {
5524     { 0, -1 },
5525     { -1, 0 },
5526     { +1, 0 },
5527     { 0, +1 }
5528   };
5529
5530   if (new_group_nr == 0)
5531     return;
5532
5533   for (i = 0; i < 4; i++)
5534   {
5535     x = ax + xy[i][0];
5536     y = ay + xy[i][1];
5537
5538     if (!IN_LEV_FIELD(x, y))
5539       continue;
5540
5541     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5542          Feld[x][y] == EL_BD_AMOEBA ||
5543          Feld[x][y] == EL_AMOEBA_DEAD) &&
5544         AmoebaNr[x][y] != new_group_nr)
5545     {
5546       int old_group_nr = AmoebaNr[x][y];
5547
5548       if (old_group_nr == 0)
5549         return;
5550
5551       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5552       AmoebaCnt[old_group_nr] = 0;
5553       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5554       AmoebaCnt2[old_group_nr] = 0;
5555
5556       for (yy = 0; yy < lev_fieldy; yy++)
5557       {
5558         for (xx = 0; xx < lev_fieldx; xx++)
5559         {
5560           if (AmoebaNr[xx][yy] == old_group_nr)
5561             AmoebaNr[xx][yy] = new_group_nr;
5562         }
5563       }
5564     }
5565   }
5566 }
5567
5568 void AmoebeUmwandeln(int ax, int ay)
5569 {
5570   int i, x, y;
5571
5572   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5573   {
5574     int group_nr = AmoebaNr[ax][ay];
5575
5576 #ifdef DEBUG
5577     if (group_nr == 0)
5578     {
5579       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5580       printf("AmoebeUmwandeln(): This should never happen!\n");
5581       return;
5582     }
5583 #endif
5584
5585     for (y = 0; y < lev_fieldy; y++)
5586     {
5587       for (x = 0; x < lev_fieldx; x++)
5588       {
5589         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5590         {
5591           AmoebaNr[x][y] = 0;
5592           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5593         }
5594       }
5595     }
5596     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5597                             SND_AMOEBA_TURNING_TO_GEM :
5598                             SND_AMOEBA_TURNING_TO_ROCK));
5599     Bang(ax, ay);
5600   }
5601   else
5602   {
5603     static int xy[4][2] =
5604     {
5605       { 0, -1 },
5606       { -1, 0 },
5607       { +1, 0 },
5608       { 0, +1 }
5609     };
5610
5611     for (i = 0; i < 4; i++)
5612     {
5613       x = ax + xy[i][0];
5614       y = ay + xy[i][1];
5615
5616       if (!IN_LEV_FIELD(x, y))
5617         continue;
5618
5619       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5620       {
5621         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5622                               SND_AMOEBA_TURNING_TO_GEM :
5623                               SND_AMOEBA_TURNING_TO_ROCK));
5624         Bang(x, y);
5625       }
5626     }
5627   }
5628 }
5629
5630 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5631 {
5632   int x, y;
5633   int group_nr = AmoebaNr[ax][ay];
5634   boolean done = FALSE;
5635
5636 #ifdef DEBUG
5637   if (group_nr == 0)
5638   {
5639     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5640     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5641     return;
5642   }
5643 #endif
5644
5645   for (y = 0; y < lev_fieldy; y++)
5646   {
5647     for (x = 0; x < lev_fieldx; x++)
5648     {
5649       if (AmoebaNr[x][y] == group_nr &&
5650           (Feld[x][y] == EL_AMOEBA_DEAD ||
5651            Feld[x][y] == EL_BD_AMOEBA ||
5652            Feld[x][y] == EL_AMOEBA_GROWING))
5653       {
5654         AmoebaNr[x][y] = 0;
5655         Feld[x][y] = new_element;
5656         InitField(x, y, FALSE);
5657         DrawLevelField(x, y);
5658         done = TRUE;
5659       }
5660     }
5661   }
5662
5663   if (done)
5664     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5665                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5666                             SND_BD_AMOEBA_TURNING_TO_GEM));
5667 }
5668
5669 void AmoebeWaechst(int x, int y)
5670 {
5671   static unsigned long sound_delay = 0;
5672   static unsigned long sound_delay_value = 0;
5673
5674   if (!MovDelay[x][y])          /* start new growing cycle */
5675   {
5676     MovDelay[x][y] = 7;
5677
5678     if (DelayReached(&sound_delay, sound_delay_value))
5679     {
5680 #if 1
5681       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5682 #else
5683       if (Store[x][y] == EL_BD_AMOEBA)
5684         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5685       else
5686         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5687 #endif
5688       sound_delay_value = 30;
5689     }
5690   }
5691
5692   if (MovDelay[x][y])           /* wait some time before growing bigger */
5693   {
5694     MovDelay[x][y]--;
5695     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5696     {
5697       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5698                                            6 - MovDelay[x][y]);
5699
5700       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5701     }
5702
5703     if (!MovDelay[x][y])
5704     {
5705       Feld[x][y] = Store[x][y];
5706       Store[x][y] = 0;
5707       DrawLevelField(x, y);
5708     }
5709   }
5710 }
5711
5712 void AmoebaDisappearing(int x, int y)
5713 {
5714   static unsigned long sound_delay = 0;
5715   static unsigned long sound_delay_value = 0;
5716
5717   if (!MovDelay[x][y])          /* start new shrinking cycle */
5718   {
5719     MovDelay[x][y] = 7;
5720
5721     if (DelayReached(&sound_delay, sound_delay_value))
5722       sound_delay_value = 30;
5723   }
5724
5725   if (MovDelay[x][y])           /* wait some time before shrinking */
5726   {
5727     MovDelay[x][y]--;
5728     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5729     {
5730       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5731                                            6 - MovDelay[x][y]);
5732
5733       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5734     }
5735
5736     if (!MovDelay[x][y])
5737     {
5738       Feld[x][y] = EL_EMPTY;
5739       DrawLevelField(x, y);
5740
5741       /* don't let mole enter this field in this cycle;
5742          (give priority to objects falling to this field from above) */
5743       Stop[x][y] = TRUE;
5744     }
5745   }
5746 }
5747
5748 void AmoebeAbleger(int ax, int ay)
5749 {
5750   int i;
5751   int element = Feld[ax][ay];
5752   int graphic = el2img(element);
5753   int newax = ax, neway = ay;
5754   static int xy[4][2] =
5755   {
5756     { 0, -1 },
5757     { -1, 0 },
5758     { +1, 0 },
5759     { 0, +1 }
5760   };
5761
5762   if (!level.amoeba_speed)
5763   {
5764     Feld[ax][ay] = EL_AMOEBA_DEAD;
5765     DrawLevelField(ax, ay);
5766     return;
5767   }
5768
5769   if (IS_ANIMATED(graphic))
5770     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5771
5772   if (!MovDelay[ax][ay])        /* start making new amoeba field */
5773     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5774
5775   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
5776   {
5777     MovDelay[ax][ay]--;
5778     if (MovDelay[ax][ay])
5779       return;
5780   }
5781
5782   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5783   {
5784     int start = RND(4);
5785     int x = ax + xy[start][0];
5786     int y = ay + xy[start][1];
5787
5788     if (!IN_LEV_FIELD(x, y))
5789       return;
5790
5791     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5792     if (IS_FREE(x, y) ||
5793         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5794     {
5795       newax = x;
5796       neway = y;
5797     }
5798
5799     if (newax == ax && neway == ay)
5800       return;
5801   }
5802   else                          /* normal or "filled" (BD style) amoeba */
5803   {
5804     int start = RND(4);
5805     boolean waiting_for_player = FALSE;
5806
5807     for (i = 0; i < 4; i++)
5808     {
5809       int j = (start + i) % 4;
5810       int x = ax + xy[j][0];
5811       int y = ay + xy[j][1];
5812
5813       if (!IN_LEV_FIELD(x, y))
5814         continue;
5815
5816       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5817       if (IS_FREE(x, y) ||
5818           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5819       {
5820         newax = x;
5821         neway = y;
5822         break;
5823       }
5824       else if (IS_PLAYER(x, y))
5825         waiting_for_player = TRUE;
5826     }
5827
5828     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5829     {
5830       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5831       {
5832         Feld[ax][ay] = EL_AMOEBA_DEAD;
5833         DrawLevelField(ax, ay);
5834         AmoebaCnt[AmoebaNr[ax][ay]]--;
5835
5836         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5837         {
5838           if (element == EL_AMOEBA_FULL)
5839             AmoebeUmwandeln(ax, ay);
5840           else if (element == EL_BD_AMOEBA)
5841             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5842         }
5843       }
5844       return;
5845     }
5846     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5847     {
5848       /* amoeba gets larger by growing in some direction */
5849
5850       int new_group_nr = AmoebaNr[ax][ay];
5851
5852 #ifdef DEBUG
5853   if (new_group_nr == 0)
5854   {
5855     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5856     printf("AmoebeAbleger(): This should never happen!\n");
5857     return;
5858   }
5859 #endif
5860
5861       AmoebaNr[newax][neway] = new_group_nr;
5862       AmoebaCnt[new_group_nr]++;
5863       AmoebaCnt2[new_group_nr]++;
5864
5865       /* if amoeba touches other amoeba(s) after growing, unify them */
5866       AmoebenVereinigen(newax, neway);
5867
5868       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5869       {
5870         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5871         return;
5872       }
5873     }
5874   }
5875
5876   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5877       (neway == lev_fieldy - 1 && newax != ax))
5878   {
5879     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5880     Store[newax][neway] = element;
5881   }
5882   else if (neway == ay)
5883   {
5884     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5885 #if 1
5886     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5887 #else
5888     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5889 #endif
5890   }
5891   else
5892   {
5893     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5894     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5895     Store[ax][ay] = EL_AMOEBA_DROP;
5896     ContinueMoving(ax, ay);
5897     return;
5898   }
5899
5900   DrawLevelField(newax, neway);
5901 }
5902
5903 void Life(int ax, int ay)
5904 {
5905   int x1, y1, x2, y2;
5906   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5907   int life_time = 40;
5908   int element = Feld[ax][ay];
5909   int graphic = el2img(element);
5910   boolean changed = FALSE;
5911
5912   if (IS_ANIMATED(graphic))
5913     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5914
5915   if (Stop[ax][ay])
5916     return;
5917
5918   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5919     MovDelay[ax][ay] = life_time;
5920
5921   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5922   {
5923     MovDelay[ax][ay]--;
5924     if (MovDelay[ax][ay])
5925       return;
5926   }
5927
5928   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5929   {
5930     int xx = ax+x1, yy = ay+y1;
5931     int nachbarn = 0;
5932
5933     if (!IN_LEV_FIELD(xx, yy))
5934       continue;
5935
5936     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5937     {
5938       int x = xx+x2, y = yy+y2;
5939
5940       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5941         continue;
5942
5943       if (((Feld[x][y] == element ||
5944             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5945            !Stop[x][y]) ||
5946           (IS_FREE(x, y) && Stop[x][y]))
5947         nachbarn++;
5948     }
5949
5950     if (xx == ax && yy == ay)           /* field in the middle */
5951     {
5952       if (nachbarn < life[0] || nachbarn > life[1])
5953       {
5954         Feld[xx][yy] = EL_EMPTY;
5955         if (!Stop[xx][yy])
5956           DrawLevelField(xx, yy);
5957         Stop[xx][yy] = TRUE;
5958         changed = TRUE;
5959       }
5960     }
5961     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5962     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5963     {                                   /* free border field */
5964       if (nachbarn >= life[2] && nachbarn <= life[3])
5965       {
5966         Feld[xx][yy] = element;
5967         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5968         if (!Stop[xx][yy])
5969           DrawLevelField(xx, yy);
5970         Stop[xx][yy] = TRUE;
5971         changed = TRUE;
5972       }
5973     }
5974   }
5975
5976   if (changed)
5977     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5978                    SND_GAME_OF_LIFE_GROWING);
5979 }
5980
5981 static void InitRobotWheel(int x, int y)
5982 {
5983   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5984 }
5985
5986 static void RunRobotWheel(int x, int y)
5987 {
5988   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5989 }
5990
5991 static void StopRobotWheel(int x, int y)
5992 {
5993   if (ZX == x && ZY == y)
5994     ZX = ZY = -1;
5995 }
5996
5997 static void InitTimegateWheel(int x, int y)
5998 {
5999   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6000 }
6001
6002 static void RunTimegateWheel(int x, int y)
6003 {
6004   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6005 }
6006
6007 void CheckExit(int x, int y)
6008 {
6009   if (local_player->gems_still_needed > 0 ||
6010       local_player->sokobanfields_still_needed > 0 ||
6011       local_player->lights_still_needed > 0)
6012   {
6013     int element = Feld[x][y];
6014     int graphic = el2img(element);
6015
6016     if (IS_ANIMATED(graphic))
6017       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6018
6019     return;
6020   }
6021
6022   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6023     return;
6024
6025   Feld[x][y] = EL_EXIT_OPENING;
6026
6027   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6028 }
6029
6030 void CheckExitSP(int x, int y)
6031 {
6032   if (local_player->gems_still_needed > 0)
6033   {
6034     int element = Feld[x][y];
6035     int graphic = el2img(element);
6036
6037     if (IS_ANIMATED(graphic))
6038       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6039
6040     return;
6041   }
6042
6043   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6044     return;
6045
6046   Feld[x][y] = EL_SP_EXIT_OPENING;
6047
6048   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6049 }
6050
6051 static void CloseAllOpenTimegates()
6052 {
6053   int x, y;
6054
6055   for (y = 0; y < lev_fieldy; y++)
6056   {
6057     for (x = 0; x < lev_fieldx; x++)
6058     {
6059       int element = Feld[x][y];
6060
6061       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6062       {
6063         Feld[x][y] = EL_TIMEGATE_CLOSING;
6064 #if 1
6065         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6066 #else
6067         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6068 #endif
6069       }
6070     }
6071   }
6072 }
6073
6074 void EdelsteinFunkeln(int x, int y)
6075 {
6076   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6077     return;
6078
6079   if (Feld[x][y] == EL_BD_DIAMOND)
6080     return;
6081
6082   if (MovDelay[x][y] == 0)      /* next animation frame */
6083     MovDelay[x][y] = 11 * !SimpleRND(500);
6084
6085   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6086   {
6087     MovDelay[x][y]--;
6088
6089     if (setup.direct_draw && MovDelay[x][y])
6090       SetDrawtoField(DRAW_BUFFERED);
6091
6092     DrawLevelElementAnimation(x, y, Feld[x][y]);
6093
6094     if (MovDelay[x][y] != 0)
6095     {
6096       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6097                                            10 - MovDelay[x][y]);
6098
6099       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6100
6101       if (setup.direct_draw)
6102       {
6103         int dest_x, dest_y;
6104
6105         dest_x = FX + SCREENX(x) * TILEX;
6106         dest_y = FY + SCREENY(y) * TILEY;
6107
6108         BlitBitmap(drawto_field, window,
6109                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6110         SetDrawtoField(DRAW_DIRECT);
6111       }
6112     }
6113   }
6114 }
6115
6116 void MauerWaechst(int x, int y)
6117 {
6118   int delay = 6;
6119
6120   if (!MovDelay[x][y])          /* next animation frame */
6121     MovDelay[x][y] = 3 * delay;
6122
6123   if (MovDelay[x][y])           /* wait some time before next frame */
6124   {
6125     MovDelay[x][y]--;
6126
6127     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6128     {
6129       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6130       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6131
6132       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6133     }
6134
6135     if (!MovDelay[x][y])
6136     {
6137       if (MovDir[x][y] == MV_LEFT)
6138       {
6139         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6140           DrawLevelField(x - 1, y);
6141       }
6142       else if (MovDir[x][y] == MV_RIGHT)
6143       {
6144         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6145           DrawLevelField(x + 1, y);
6146       }
6147       else if (MovDir[x][y] == MV_UP)
6148       {
6149         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6150           DrawLevelField(x, y - 1);
6151       }
6152       else
6153       {
6154         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6155           DrawLevelField(x, y + 1);
6156       }
6157
6158       Feld[x][y] = Store[x][y];
6159       Store[x][y] = 0;
6160       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6161       DrawLevelField(x, y);
6162     }
6163   }
6164 }
6165
6166 void MauerAbleger(int ax, int ay)
6167 {
6168   int element = Feld[ax][ay];
6169   int graphic = el2img(element);
6170   boolean oben_frei = FALSE, unten_frei = FALSE;
6171   boolean links_frei = FALSE, rechts_frei = FALSE;
6172   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6173   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6174   boolean new_wall = FALSE;
6175
6176   if (IS_ANIMATED(graphic))
6177     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6178
6179   if (!MovDelay[ax][ay])        /* start building new wall */
6180     MovDelay[ax][ay] = 6;
6181
6182   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6183   {
6184     MovDelay[ax][ay]--;
6185     if (MovDelay[ax][ay])
6186       return;
6187   }
6188
6189   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6190     oben_frei = TRUE;
6191   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6192     unten_frei = TRUE;
6193   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6194     links_frei = TRUE;
6195   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6196     rechts_frei = TRUE;
6197
6198   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6199       element == EL_EXPANDABLE_WALL_ANY)
6200   {
6201     if (oben_frei)
6202     {
6203       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6204       Store[ax][ay-1] = element;
6205       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6206       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6207         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6208                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6209       new_wall = TRUE;
6210     }
6211     if (unten_frei)
6212     {
6213       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6214       Store[ax][ay+1] = element;
6215       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6216       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6217         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6218                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6219       new_wall = TRUE;
6220     }
6221   }
6222
6223   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6224       element == EL_EXPANDABLE_WALL_ANY ||
6225       element == EL_EXPANDABLE_WALL)
6226   {
6227     if (links_frei)
6228     {
6229       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6230       Store[ax-1][ay] = element;
6231       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6232       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6233         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6234                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6235       new_wall = TRUE;
6236     }
6237
6238     if (rechts_frei)
6239     {
6240       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6241       Store[ax+1][ay] = element;
6242       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6243       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6244         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6245                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6246       new_wall = TRUE;
6247     }
6248   }
6249
6250   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6251     DrawLevelField(ax, ay);
6252
6253   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6254     oben_massiv = TRUE;
6255   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6256     unten_massiv = TRUE;
6257   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6258     links_massiv = TRUE;
6259   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6260     rechts_massiv = TRUE;
6261
6262   if (((oben_massiv && unten_massiv) ||
6263        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6264        element == EL_EXPANDABLE_WALL) &&
6265       ((links_massiv && rechts_massiv) ||
6266        element == EL_EXPANDABLE_WALL_VERTICAL))
6267     Feld[ax][ay] = EL_WALL;
6268
6269   if (new_wall)
6270 #if 1
6271     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6272 #else
6273     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6274 #endif
6275 }
6276
6277 void CheckForDragon(int x, int y)
6278 {
6279   int i, j;
6280   boolean dragon_found = FALSE;
6281   static int xy[4][2] =
6282   {
6283     { 0, -1 },
6284     { -1, 0 },
6285     { +1, 0 },
6286     { 0, +1 }
6287   };
6288
6289   for (i = 0; i < 4; i++)
6290   {
6291     for (j = 0; j < 4; j++)
6292     {
6293       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6294
6295       if (IN_LEV_FIELD(xx, yy) &&
6296           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6297       {
6298         if (Feld[xx][yy] == EL_DRAGON)
6299           dragon_found = TRUE;
6300       }
6301       else
6302         break;
6303     }
6304   }
6305
6306   if (!dragon_found)
6307   {
6308     for (i = 0; i < 4; i++)
6309     {
6310       for (j = 0; j < 3; j++)
6311       {
6312         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6313   
6314         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6315         {
6316           Feld[xx][yy] = EL_EMPTY;
6317           DrawLevelField(xx, yy);
6318         }
6319         else
6320           break;
6321       }
6322     }
6323   }
6324 }
6325
6326 static void InitBuggyBase(int x, int y)
6327 {
6328   int element = Feld[x][y];
6329   int activating_delay = FRAMES_PER_SECOND / 4;
6330
6331   ChangeDelay[x][y] =
6332     (element == EL_SP_BUGGY_BASE ?
6333      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6334      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6335      activating_delay :
6336      element == EL_SP_BUGGY_BASE_ACTIVE ?
6337      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6338 }
6339
6340 static void WarnBuggyBase(int x, int y)
6341 {
6342   int i;
6343   static int xy[4][2] =
6344   {
6345     { 0, -1 },
6346     { -1, 0 },
6347     { +1, 0 },
6348     { 0, +1 }
6349   };
6350
6351   for (i = 0; i < 4; i++)
6352   {
6353     int xx = x + xy[i][0], yy = y + xy[i][1];
6354
6355     if (IS_PLAYER(xx, yy))
6356     {
6357       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6358
6359       break;
6360     }
6361   }
6362 }
6363
6364 static void InitTrap(int x, int y)
6365 {
6366   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6367 }
6368
6369 static void ActivateTrap(int x, int y)
6370 {
6371   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6372 }
6373
6374 static void ChangeActiveTrap(int x, int y)
6375 {
6376   int graphic = IMG_TRAP_ACTIVE;
6377
6378   /* if new animation frame was drawn, correct crumbled sand border */
6379   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6380     DrawLevelFieldCrumbledSand(x, y);
6381 }
6382
6383 static void ChangeElementNowExt(int x, int y, int target_element)
6384 {
6385   int previous_move_direction = MovDir[x][y];
6386
6387   /* check if element under player changes from accessible to unaccessible
6388      (needed for special case of dropping element which then changes) */
6389   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6390       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6391   {
6392     Bang(x, y);
6393     return;
6394   }
6395
6396   RemoveField(x, y);
6397   Feld[x][y] = target_element;
6398
6399   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6400
6401   ResetGfxAnimation(x, y);
6402   ResetRandomAnimationValue(x, y);
6403
6404   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6405     MovDir[x][y] = previous_move_direction;
6406
6407   InitField(x, y, FALSE);
6408   if (CAN_MOVE(Feld[x][y]))
6409     InitMovDir(x, y);
6410
6411   DrawLevelField(x, y);
6412
6413   if (GFX_CRUMBLED(Feld[x][y]))
6414     DrawLevelFieldCrumbledSandNeighbours(x, y);
6415
6416   TestIfBadThingTouchesHero(x, y);
6417   TestIfPlayerTouchesCustomElement(x, y);
6418   TestIfElementTouchesCustomElement(x, y);
6419
6420   if (ELEM_IS_PLAYER(target_element))
6421     RelocatePlayer(x, y, target_element);
6422 }
6423
6424 static boolean ChangeElementNow(int x, int y, int element, int page)
6425 {
6426   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6427
6428   /* always use default change event to prevent running into a loop */
6429   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6430     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6431
6432   /* do not change already changed elements with same change event */
6433 #if 0
6434   if (Changed[x][y] & ChangeEvent[x][y])
6435     return FALSE;
6436 #else
6437   if (Changed[x][y])
6438     return FALSE;
6439 #endif
6440
6441   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6442
6443   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6444
6445   if (change->explode)
6446   {
6447     Bang(x, y);
6448
6449     return TRUE;
6450   }
6451
6452   if (change->use_content)
6453   {
6454     boolean complete_change = TRUE;
6455     boolean can_change[3][3];
6456     int xx, yy;
6457
6458     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6459     {
6460       boolean half_destructible;
6461       int ex = x + xx - 1;
6462       int ey = y + yy - 1;
6463       int e;
6464
6465       can_change[xx][yy] = TRUE;
6466
6467       if (ex == x && ey == y)   /* do not check changing element itself */
6468         continue;
6469
6470       if (change->content[xx][yy] == EL_EMPTY_SPACE)
6471       {
6472         can_change[xx][yy] = FALSE;     /* do not change empty borders */
6473
6474         continue;
6475       }
6476
6477       if (!IN_LEV_FIELD(ex, ey))
6478       {
6479         can_change[xx][yy] = FALSE;
6480         complete_change = FALSE;
6481
6482         continue;
6483       }
6484
6485       e = Feld[ex][ey];
6486
6487       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6488         e = MovingOrBlocked2Element(ex, ey);
6489
6490       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6491
6492       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
6493           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6494           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6495       {
6496         can_change[xx][yy] = FALSE;
6497         complete_change = FALSE;
6498       }
6499     }
6500
6501     if (!change->only_complete || complete_change)
6502     {
6503       boolean something_has_changed = FALSE;
6504
6505       if (change->only_complete && change->use_random_change &&
6506           RND(100) < change->random)
6507         return FALSE;
6508
6509       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6510       {
6511         int ex = x + xx - 1;
6512         int ey = y + yy - 1;
6513
6514         if (can_change[xx][yy] && (!change->use_random_change ||
6515                                    RND(100) < change->random))
6516         {
6517           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6518             RemoveMovingField(ex, ey);
6519
6520           ChangeEvent[ex][ey] = ChangeEvent[x][y];
6521
6522           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6523
6524           something_has_changed = TRUE;
6525
6526           /* for symmetry reasons, freeze newly created border elements */
6527           if (ex != x || ey != y)
6528             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
6529         }
6530       }
6531
6532       if (something_has_changed)
6533         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6534     }
6535   }
6536   else
6537   {
6538     ChangeElementNowExt(x, y, change->target_element);
6539
6540     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6541   }
6542
6543   return TRUE;
6544 }
6545
6546 static void ChangeElement(int x, int y, int page)
6547 {
6548   int element = MovingOrBlocked2Element(x, y);
6549   struct ElementInfo *ei = &element_info[element];
6550   struct ElementChangeInfo *change = &ei->change_page[page];
6551
6552 #if 0
6553 #ifdef DEBUG
6554   if (!CAN_CHANGE(element))
6555   {
6556     printf("\n\n");
6557     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6558            x, y, element, element_info[element].token_name);
6559     printf("ChangeElement(): This should never happen!\n");
6560     printf("\n\n");
6561   }
6562 #endif
6563 #endif
6564
6565   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6566   {
6567     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6568                          RND(change->delay_random * change->delay_frames)) + 1;
6569
6570     ResetGfxAnimation(x, y);
6571     ResetRandomAnimationValue(x, y);
6572
6573     if (change->pre_change_function)
6574       change->pre_change_function(x, y);
6575   }
6576
6577   ChangeDelay[x][y]--;
6578
6579   if (ChangeDelay[x][y] != 0)           /* continue element change */
6580   {
6581     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6582
6583     if (IS_ANIMATED(graphic))
6584       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6585
6586     if (change->change_function)
6587       change->change_function(x, y);
6588   }
6589   else                                  /* finish element change */
6590   {
6591     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6592     {
6593       page = ChangePage[x][y];
6594       ChangePage[x][y] = -1;
6595     }
6596
6597 #if 0
6598     if (IS_MOVING(x, y) && !change->explode)
6599 #else
6600     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6601 #endif
6602     {
6603       ChangeDelay[x][y] = 1;            /* try change after next move step */
6604       ChangePage[x][y] = page;          /* remember page to use for change */
6605
6606       return;
6607     }
6608
6609     if (ChangeElementNow(x, y, element, page))
6610     {
6611       if (change->post_change_function)
6612         change->post_change_function(x, y);
6613     }
6614   }
6615 }
6616
6617 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6618                                                int trigger_element,
6619                                                int trigger_side,
6620                                                int trigger_event)
6621 {
6622   int i, j, x, y;
6623
6624   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6625     return FALSE;
6626
6627   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6628   {
6629     int element = EL_CUSTOM_START + i;
6630
6631     boolean change_element = FALSE;
6632     int page = 0;
6633
6634     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6635       continue;
6636
6637     for (j = 0; j < element_info[element].num_change_pages; j++)
6638     {
6639       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6640
6641       if (change->can_change &&
6642 #if 1
6643           change->events & CH_EVENT_BIT(trigger_event) &&
6644 #endif
6645           change->sides & trigger_side &&
6646 #if 1
6647           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6648 #else
6649           change->trigger_element == trigger_element
6650 #endif
6651           )
6652       {
6653 #if 0
6654         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6655           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6656                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6657 #endif
6658
6659         change_element = TRUE;
6660         page = j;
6661
6662         break;
6663       }
6664     }
6665
6666     if (!change_element)
6667       continue;
6668
6669     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6670     {
6671 #if 0
6672       if (x == lx && y == ly)   /* do not change trigger element itself */
6673         continue;
6674 #endif
6675
6676       if (Feld[x][y] == element)
6677       {
6678         ChangeDelay[x][y] = 1;
6679         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6680         ChangeElement(x, y, page);
6681       }
6682     }
6683   }
6684
6685   return TRUE;
6686 }
6687
6688 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6689                                            int trigger_event)
6690 {
6691   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6692                                          trigger_event);
6693 }
6694
6695 static boolean CheckElementSideChange(int x, int y, int element, int side,
6696                                       int trigger_event, int page)
6697 {
6698   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6699     return FALSE;
6700
6701   if (Feld[x][y] == EL_BLOCKED)
6702   {
6703     Blocked2Moving(x, y, &x, &y);
6704     element = Feld[x][y];
6705   }
6706
6707 #if 1
6708   if (page < 0)
6709   {
6710     boolean change_element = FALSE;
6711     int i;
6712
6713     for (i = 0; i < element_info[element].num_change_pages; i++)
6714     {
6715       struct ElementChangeInfo *change = &element_info[element].change_page[i];
6716
6717       if (change->can_change &&
6718           change->events & CH_EVENT_BIT(trigger_event) &&
6719           change->sides & side)
6720       {
6721         change_element = TRUE;
6722         page = i;
6723
6724         break;
6725       }
6726     }
6727
6728     if (!change_element)
6729       return FALSE;
6730   }
6731
6732 #else
6733
6734   /* !!! this check misses pages with same event, but different side !!! */
6735
6736   if (page < 0)
6737     page = element_info[element].event_page_nr[trigger_event];
6738
6739   if (!(element_info[element].change_page[page].sides & side))
6740     return FALSE;
6741 #endif
6742
6743   ChangeDelay[x][y] = 1;
6744   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6745   ChangeElement(x, y, page);
6746
6747   return TRUE;
6748 }
6749
6750 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6751 {
6752   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6753 }
6754
6755 static void PlayPlayerSound(struct PlayerInfo *player)
6756 {
6757   int jx = player->jx, jy = player->jy;
6758   int element = player->element_nr;
6759   int last_action = player->last_action_waiting;
6760   int action = player->action_waiting;
6761
6762   if (player->is_waiting)
6763   {
6764     if (action != last_action)
6765       PlayLevelSoundElementAction(jx, jy, element, action);
6766     else
6767       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6768   }
6769   else
6770   {
6771     if (action != last_action)
6772       StopSound(element_info[element].sound[last_action]);
6773
6774     if (last_action == ACTION_SLEEPING)
6775       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6776   }
6777 }
6778
6779 static void PlayAllPlayersSound()
6780 {
6781   int i;
6782
6783   for (i = 0; i < MAX_PLAYERS; i++)
6784     if (stored_player[i].active)
6785       PlayPlayerSound(&stored_player[i]);
6786 }
6787
6788 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6789 {
6790   boolean last_waiting = player->is_waiting;
6791   int move_dir = player->MovDir;
6792
6793   player->last_action_waiting = player->action_waiting;
6794
6795   if (is_waiting)
6796   {
6797     if (!last_waiting)          /* not waiting -> waiting */
6798     {
6799       player->is_waiting = TRUE;
6800
6801       player->frame_counter_bored =
6802         FrameCounter +
6803         game.player_boring_delay_fixed +
6804         SimpleRND(game.player_boring_delay_random);
6805       player->frame_counter_sleeping =
6806         FrameCounter +
6807         game.player_sleeping_delay_fixed +
6808         SimpleRND(game.player_sleeping_delay_random);
6809
6810       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6811     }
6812
6813     if (game.player_sleeping_delay_fixed +
6814         game.player_sleeping_delay_random > 0 &&
6815         player->anim_delay_counter == 0 &&
6816         player->post_delay_counter == 0 &&
6817         FrameCounter >= player->frame_counter_sleeping)
6818       player->is_sleeping = TRUE;
6819     else if (game.player_boring_delay_fixed +
6820              game.player_boring_delay_random > 0 &&
6821              FrameCounter >= player->frame_counter_bored)
6822       player->is_bored = TRUE;
6823
6824     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6825                               player->is_bored ? ACTION_BORING :
6826                               ACTION_WAITING);
6827
6828     if (player->is_sleeping)
6829     {
6830       if (player->num_special_action_sleeping > 0)
6831       {
6832         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6833         {
6834           int last_special_action = player->special_action_sleeping;
6835           int num_special_action = player->num_special_action_sleeping;
6836           int special_action =
6837             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6838              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6839              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6840              last_special_action + 1 : ACTION_SLEEPING);
6841           int special_graphic =
6842             el_act_dir2img(player->element_nr, special_action, move_dir);
6843
6844           player->anim_delay_counter =
6845             graphic_info[special_graphic].anim_delay_fixed +
6846             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6847           player->post_delay_counter =
6848             graphic_info[special_graphic].post_delay_fixed +
6849             SimpleRND(graphic_info[special_graphic].post_delay_random);
6850
6851           player->special_action_sleeping = special_action;
6852         }
6853
6854         if (player->anim_delay_counter > 0)
6855         {
6856           player->action_waiting = player->special_action_sleeping;
6857           player->anim_delay_counter--;
6858         }
6859         else if (player->post_delay_counter > 0)
6860         {
6861           player->post_delay_counter--;
6862         }
6863       }
6864     }
6865     else if (player->is_bored)
6866     {
6867       if (player->num_special_action_bored > 0)
6868       {
6869         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6870         {
6871           int special_action =
6872             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6873           int special_graphic =
6874             el_act_dir2img(player->element_nr, special_action, move_dir);
6875
6876           player->anim_delay_counter =
6877             graphic_info[special_graphic].anim_delay_fixed +
6878             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6879           player->post_delay_counter =
6880             graphic_info[special_graphic].post_delay_fixed +
6881             SimpleRND(graphic_info[special_graphic].post_delay_random);
6882
6883           player->special_action_bored = special_action;
6884         }
6885
6886         if (player->anim_delay_counter > 0)
6887         {
6888           player->action_waiting = player->special_action_bored;
6889           player->anim_delay_counter--;
6890         }
6891         else if (player->post_delay_counter > 0)
6892         {
6893           player->post_delay_counter--;
6894         }
6895       }
6896     }
6897   }
6898   else if (last_waiting)        /* waiting -> not waiting */
6899   {
6900     player->is_waiting = FALSE;
6901     player->is_bored = FALSE;
6902     player->is_sleeping = FALSE;
6903
6904     player->frame_counter_bored = -1;
6905     player->frame_counter_sleeping = -1;
6906
6907     player->anim_delay_counter = 0;
6908     player->post_delay_counter = 0;
6909
6910     player->action_waiting = ACTION_DEFAULT;
6911
6912     player->special_action_bored = ACTION_DEFAULT;
6913     player->special_action_sleeping = ACTION_DEFAULT;
6914   }
6915 }
6916
6917 #if 1
6918 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6919 {
6920 #if 0
6921   static byte stored_player_action[MAX_PLAYERS];
6922   static int num_stored_actions = 0;
6923 #endif
6924   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6925   int left      = player_action & JOY_LEFT;
6926   int right     = player_action & JOY_RIGHT;
6927   int up        = player_action & JOY_UP;
6928   int down      = player_action & JOY_DOWN;
6929   int button1   = player_action & JOY_BUTTON_1;
6930   int button2   = player_action & JOY_BUTTON_2;
6931   int dx        = (left ? -1    : right ? 1     : 0);
6932   int dy        = (up   ? -1    : down  ? 1     : 0);
6933
6934 #if 0
6935   stored_player_action[player->index_nr] = 0;
6936   num_stored_actions++;
6937 #endif
6938
6939 #if 0
6940   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6941 #endif
6942
6943   if (!player->active || tape.pausing)
6944     return 0;
6945
6946 #if 0
6947   printf("::: [%d %d %d %d] [%d %d]\n",
6948          left, right, up, down, button1, button2);
6949 #endif
6950
6951   if (player_action)
6952   {
6953 #if 0
6954     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6955 #endif
6956
6957     if (button1)
6958       snapped = SnapField(player, dx, dy);
6959     else
6960     {
6961       if (button2)
6962         dropped = DropElement(player);
6963
6964       moved = MovePlayer(player, dx, dy);
6965     }
6966
6967     if (tape.single_step && tape.recording && !tape.pausing)
6968     {
6969       if (button1 || (dropped && !moved))
6970       {
6971         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6972         SnapField(player, 0, 0);                /* stop snapping */
6973       }
6974     }
6975
6976     SetPlayerWaiting(player, FALSE);
6977
6978 #if 1
6979     return player_action;
6980 #else
6981     stored_player_action[player->index_nr] = player_action;
6982 #endif
6983   }
6984   else
6985   {
6986 #if 0
6987     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6988 #endif
6989
6990     /* no actions for this player (no input at player's configured device) */
6991
6992     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6993     SnapField(player, 0, 0);
6994     CheckGravityMovement(player);
6995
6996     if (player->MovPos == 0)
6997       SetPlayerWaiting(player, TRUE);
6998
6999     if (player->MovPos == 0)    /* needed for tape.playing */
7000       player->is_moving = FALSE;
7001
7002     player->is_dropping = FALSE;
7003
7004     return 0;
7005   }
7006
7007 #if 0
7008   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7009   {
7010     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7011
7012     TapeRecordAction(stored_player_action);
7013     num_stored_actions = 0;
7014   }
7015 #endif
7016 }
7017
7018 #else
7019
7020 static void PlayerActions(struct PlayerInfo *player, byte player_action)
7021 {
7022   static byte stored_player_action[MAX_PLAYERS];
7023   static int num_stored_actions = 0;
7024   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7025   int left      = player_action & JOY_LEFT;
7026   int right     = player_action & JOY_RIGHT;
7027   int up        = player_action & JOY_UP;
7028   int down      = player_action & JOY_DOWN;
7029   int button1   = player_action & JOY_BUTTON_1;
7030   int button2   = player_action & JOY_BUTTON_2;
7031   int dx        = (left ? -1    : right ? 1     : 0);
7032   int dy        = (up   ? -1    : down  ? 1     : 0);
7033
7034   stored_player_action[player->index_nr] = 0;
7035   num_stored_actions++;
7036
7037   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7038
7039   if (!player->active || tape.pausing)
7040     return;
7041
7042   if (player_action)
7043   {
7044     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7045
7046     if (button1)
7047       snapped = SnapField(player, dx, dy);
7048     else
7049     {
7050       if (button2)
7051         dropped = DropElement(player);
7052
7053       moved = MovePlayer(player, dx, dy);
7054     }
7055
7056     if (tape.single_step && tape.recording && !tape.pausing)
7057     {
7058       if (button1 || (dropped && !moved))
7059       {
7060         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7061         SnapField(player, 0, 0);                /* stop snapping */
7062       }
7063     }
7064
7065     stored_player_action[player->index_nr] = player_action;
7066   }
7067   else
7068   {
7069     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7070
7071     /* no actions for this player (no input at player's configured device) */
7072
7073     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
7074     SnapField(player, 0, 0);
7075     CheckGravityMovement(player);
7076
7077     if (player->MovPos == 0)
7078       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7079
7080     if (player->MovPos == 0)    /* needed for tape.playing */
7081       player->is_moving = FALSE;
7082   }
7083
7084   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7085   {
7086     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7087
7088     TapeRecordAction(stored_player_action);
7089     num_stored_actions = 0;
7090   }
7091 }
7092 #endif
7093
7094 void GameActions()
7095 {
7096   static unsigned long action_delay = 0;
7097   unsigned long action_delay_value;
7098   int magic_wall_x = 0, magic_wall_y = 0;
7099   int i, x, y, element, graphic;
7100   byte *recorded_player_action;
7101   byte summarized_player_action = 0;
7102 #if 1
7103   byte tape_action[MAX_PLAYERS];
7104 #endif
7105
7106   if (game_status != GAME_MODE_PLAYING)
7107     return;
7108
7109   action_delay_value =
7110     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7111
7112   if (tape.playing && tape.index_search && !tape.pausing)
7113     action_delay_value = 0;
7114
7115   /* ---------- main game synchronization point ---------- */
7116
7117   WaitUntilDelayReached(&action_delay, action_delay_value);
7118
7119   if (network_playing && !network_player_action_received)
7120   {
7121     /*
7122 #ifdef DEBUG
7123     printf("DEBUG: try to get network player actions in time\n");
7124 #endif
7125     */
7126
7127 #if defined(PLATFORM_UNIX)
7128     /* last chance to get network player actions without main loop delay */
7129     HandleNetworking();
7130 #endif
7131
7132     if (game_status != GAME_MODE_PLAYING)
7133       return;
7134
7135     if (!network_player_action_received)
7136     {
7137       /*
7138 #ifdef DEBUG
7139       printf("DEBUG: failed to get network player actions in time\n");
7140 #endif
7141       */
7142       return;
7143     }
7144   }
7145
7146   if (tape.pausing)
7147     return;
7148
7149 #if 0
7150   printf("::: getting new tape action [%d]\n", FrameCounter);
7151 #endif
7152
7153   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7154
7155   for (i = 0; i < MAX_PLAYERS; i++)
7156   {
7157     summarized_player_action |= stored_player[i].action;
7158
7159     if (!network_playing)
7160       stored_player[i].effective_action = stored_player[i].action;
7161   }
7162
7163 #if defined(PLATFORM_UNIX)
7164   if (network_playing)
7165     SendToServer_MovePlayer(summarized_player_action);
7166 #endif
7167
7168   if (!options.network && !setup.team_mode)
7169     local_player->effective_action = summarized_player_action;
7170
7171   for (i = 0; i < MAX_PLAYERS; i++)
7172   {
7173     int actual_player_action = stored_player[i].effective_action;
7174
7175     if (stored_player[i].programmed_action)
7176       actual_player_action = stored_player[i].programmed_action;
7177
7178     if (recorded_player_action)
7179       actual_player_action = recorded_player_action[i];
7180
7181     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7182
7183     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7184       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7185
7186     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7187   }
7188
7189 #if 1
7190   if (tape.recording)
7191     TapeRecordAction(tape_action);
7192 #endif
7193
7194   network_player_action_received = FALSE;
7195
7196   ScrollScreen(NULL, SCROLL_GO_ON);
7197
7198 #if 0
7199   FrameCounter++;
7200   TimeFrames++;
7201
7202   for (i = 0; i < MAX_PLAYERS; i++)
7203     stored_player[i].Frame++;
7204 #endif
7205
7206 #if 1
7207   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7208   {
7209     for (i = 0; i < MAX_PLAYERS; i++)
7210     {
7211       struct PlayerInfo *player = &stored_player[i];
7212       int x = player->jx;
7213       int y = player->jy;
7214
7215       if (player->active && player->is_pushing && player->is_moving &&
7216           IS_MOVING(x, y))
7217       {
7218         ContinueMoving(x, y);
7219
7220         /* continue moving after pushing (this is actually a bug) */
7221         if (!IS_MOVING(x, y))
7222         {
7223           Stop[x][y] = FALSE;
7224         }
7225       }
7226     }
7227   }
7228 #endif
7229
7230   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7231   {
7232     Changed[x][y] = CE_BITMASK_DEFAULT;
7233     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7234
7235 #if DEBUG
7236     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7237     {
7238       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7239       printf("GameActions(): This should never happen!\n");
7240
7241       ChangePage[x][y] = -1;
7242     }
7243 #endif
7244
7245     Stop[x][y] = FALSE;
7246     if (WasJustMoving[x][y] > 0)
7247       WasJustMoving[x][y]--;
7248     if (WasJustFalling[x][y] > 0)
7249       WasJustFalling[x][y]--;
7250
7251     GfxFrame[x][y]++;
7252
7253 #if 1
7254     /* reset finished pushing action (not done in ContinueMoving() to allow
7255        continous pushing animation for elements with zero push delay) */
7256     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7257     {
7258       ResetGfxAnimation(x, y);
7259       DrawLevelField(x, y);
7260     }
7261 #endif
7262
7263 #if DEBUG
7264     if (IS_BLOCKED(x, y))
7265     {
7266       int oldx, oldy;
7267
7268       Blocked2Moving(x, y, &oldx, &oldy);
7269       if (!IS_MOVING(oldx, oldy))
7270       {
7271         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7272         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7273         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7274         printf("GameActions(): This should never happen!\n");
7275       }
7276     }
7277 #endif
7278   }
7279
7280   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7281   {
7282     element = Feld[x][y];
7283 #if 1
7284     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7285 #else
7286     graphic = el2img(element);
7287 #endif
7288
7289 #if 0
7290     if (element == -1)
7291     {
7292       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7293
7294       element = graphic = 0;
7295     }
7296 #endif
7297
7298     if (graphic_info[graphic].anim_global_sync)
7299       GfxFrame[x][y] = FrameCounter;
7300
7301     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7302         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7303       ResetRandomAnimationValue(x, y);
7304
7305     SetRandomAnimationValue(x, y);
7306
7307 #if 1
7308     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7309 #endif
7310
7311     if (IS_INACTIVE(element))
7312     {
7313       if (IS_ANIMATED(graphic))
7314         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7315
7316       continue;
7317     }
7318
7319 #if 1
7320     /* this may take place after moving, so 'element' may have changed */
7321 #if 0
7322     if (IS_CHANGING(x, y))
7323 #else
7324     if (IS_CHANGING(x, y) &&
7325         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7326 #endif
7327     {
7328 #if 0
7329       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7330                     element_info[element].event_page_nr[CE_DELAY]);
7331 #else
7332       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7333 #endif
7334
7335       element = Feld[x][y];
7336       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7337     }
7338 #endif
7339
7340     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7341     {
7342       StartMoving(x, y);
7343
7344 #if 1
7345       element = Feld[x][y];
7346       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7347 #if 0
7348       if (element == EL_MOLE)
7349         printf("::: %d, %d, %d [%d]\n",
7350                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7351                GfxAction[x][y]);
7352 #endif
7353 #if 0
7354       if (element == EL_YAMYAM)
7355         printf("::: %d, %d, %d\n",
7356                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7357 #endif
7358 #endif
7359
7360       if (IS_ANIMATED(graphic) &&
7361           !IS_MOVING(x, y) &&
7362           !Stop[x][y])
7363       {
7364         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7365
7366 #if 0
7367         if (element == EL_BUG)
7368           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7369 #endif
7370
7371 #if 0
7372         if (element == EL_MOLE)
7373           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7374 #endif
7375       }
7376
7377       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7378         EdelsteinFunkeln(x, y);
7379     }
7380     else if ((element == EL_ACID ||
7381               element == EL_EXIT_OPEN ||
7382               element == EL_SP_EXIT_OPEN ||
7383               element == EL_SP_TERMINAL ||
7384               element == EL_SP_TERMINAL_ACTIVE ||
7385               element == EL_EXTRA_TIME ||
7386               element == EL_SHIELD_NORMAL ||
7387               element == EL_SHIELD_DEADLY) &&
7388              IS_ANIMATED(graphic))
7389       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7390     else if (IS_MOVING(x, y))
7391       ContinueMoving(x, y);
7392     else if (IS_ACTIVE_BOMB(element))
7393       CheckDynamite(x, y);
7394 #if 0
7395     else if (element == EL_EXPLOSION && !game.explosions_delayed)
7396       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7397 #endif
7398     else if (element == EL_AMOEBA_GROWING)
7399       AmoebeWaechst(x, y);
7400     else if (element == EL_AMOEBA_SHRINKING)
7401       AmoebaDisappearing(x, y);
7402
7403 #if !USE_NEW_AMOEBA_CODE
7404     else if (IS_AMOEBALIVE(element))
7405       AmoebeAbleger(x, y);
7406 #endif
7407
7408     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7409       Life(x, y);
7410     else if (element == EL_EXIT_CLOSED)
7411       CheckExit(x, y);
7412     else if (element == EL_SP_EXIT_CLOSED)
7413       CheckExitSP(x, y);
7414     else if (element == EL_EXPANDABLE_WALL_GROWING)
7415       MauerWaechst(x, y);
7416     else if (element == EL_EXPANDABLE_WALL ||
7417              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7418              element == EL_EXPANDABLE_WALL_VERTICAL ||
7419              element == EL_EXPANDABLE_WALL_ANY)
7420       MauerAbleger(x, y);
7421     else if (element == EL_FLAMES)
7422       CheckForDragon(x, y);
7423 #if 0
7424     else if (IS_AUTO_CHANGING(element))
7425       ChangeElement(x, y);
7426 #endif
7427     else if (element == EL_EXPLOSION)
7428       ; /* drawing of correct explosion animation is handled separately */
7429     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7430       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7431
7432 #if 0
7433     /* this may take place after moving, so 'element' may have changed */
7434     if (IS_AUTO_CHANGING(Feld[x][y]))
7435       ChangeElement(x, y);
7436 #endif
7437
7438     if (IS_BELT_ACTIVE(element))
7439       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7440
7441     if (game.magic_wall_active)
7442     {
7443       int jx = local_player->jx, jy = local_player->jy;
7444
7445       /* play the element sound at the position nearest to the player */
7446       if ((element == EL_MAGIC_WALL_FULL ||
7447            element == EL_MAGIC_WALL_ACTIVE ||
7448            element == EL_MAGIC_WALL_EMPTYING ||
7449            element == EL_BD_MAGIC_WALL_FULL ||
7450            element == EL_BD_MAGIC_WALL_ACTIVE ||
7451            element == EL_BD_MAGIC_WALL_EMPTYING) &&
7452           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7453       {
7454         magic_wall_x = x;
7455         magic_wall_y = y;
7456       }
7457     }
7458   }
7459
7460 #if USE_NEW_AMOEBA_CODE
7461   /* new experimental amoeba growth stuff */
7462 #if 1
7463   if (!(FrameCounter % 8))
7464 #endif
7465   {
7466     static unsigned long random = 1684108901;
7467
7468     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7469     {
7470 #if 0
7471       x = (random >> 10) % lev_fieldx;
7472       y = (random >> 20) % lev_fieldy;
7473 #else
7474       x = RND(lev_fieldx);
7475       y = RND(lev_fieldy);
7476 #endif
7477       element = Feld[x][y];
7478
7479       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7480       if (!IS_PLAYER(x,y) &&
7481           (element == EL_EMPTY ||
7482            element == EL_SAND ||
7483            element == EL_QUICKSAND_EMPTY ||
7484            element == EL_ACID_SPLASH_LEFT ||
7485            element == EL_ACID_SPLASH_RIGHT))
7486       {
7487         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7488             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7489             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7490             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7491           Feld[x][y] = EL_AMOEBA_DROP;
7492       }
7493
7494       random = random * 129 + 1;
7495     }
7496   }
7497 #endif
7498
7499 #if 0
7500   if (game.explosions_delayed)
7501 #endif
7502   {
7503     game.explosions_delayed = FALSE;
7504
7505     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7506     {
7507       element = Feld[x][y];
7508
7509       if (ExplodeField[x][y])
7510         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7511       else if (element == EL_EXPLOSION)
7512         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7513
7514       ExplodeField[x][y] = EX_NO_EXPLOSION;
7515     }
7516
7517     game.explosions_delayed = TRUE;
7518   }
7519
7520   if (game.magic_wall_active)
7521   {
7522     if (!(game.magic_wall_time_left % 4))
7523     {
7524       int element = Feld[magic_wall_x][magic_wall_y];
7525
7526       if (element == EL_BD_MAGIC_WALL_FULL ||
7527           element == EL_BD_MAGIC_WALL_ACTIVE ||
7528           element == EL_BD_MAGIC_WALL_EMPTYING)
7529         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7530       else
7531         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7532     }
7533
7534     if (game.magic_wall_time_left > 0)
7535     {
7536       game.magic_wall_time_left--;
7537       if (!game.magic_wall_time_left)
7538       {
7539         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7540         {
7541           element = Feld[x][y];
7542
7543           if (element == EL_MAGIC_WALL_ACTIVE ||
7544               element == EL_MAGIC_WALL_FULL)
7545           {
7546             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7547             DrawLevelField(x, y);
7548           }
7549           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7550                    element == EL_BD_MAGIC_WALL_FULL)
7551           {
7552             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7553             DrawLevelField(x, y);
7554           }
7555         }
7556
7557         game.magic_wall_active = FALSE;
7558       }
7559     }
7560   }
7561
7562   if (game.light_time_left > 0)
7563   {
7564     game.light_time_left--;
7565
7566     if (game.light_time_left == 0)
7567       RedrawAllLightSwitchesAndInvisibleElements();
7568   }
7569
7570   if (game.timegate_time_left > 0)
7571   {
7572     game.timegate_time_left--;
7573
7574     if (game.timegate_time_left == 0)
7575       CloseAllOpenTimegates();
7576   }
7577
7578   for (i = 0; i < MAX_PLAYERS; i++)
7579   {
7580     struct PlayerInfo *player = &stored_player[i];
7581
7582     if (SHIELD_ON(player))
7583     {
7584       if (player->shield_deadly_time_left)
7585         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7586       else if (player->shield_normal_time_left)
7587         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7588     }
7589   }
7590
7591   if (TimeFrames >= FRAMES_PER_SECOND)
7592   {
7593     TimeFrames = 0;
7594     TimePlayed++;
7595
7596     for (i = 0; i < MAX_PLAYERS; i++)
7597     {
7598       struct PlayerInfo *player = &stored_player[i];
7599
7600       if (SHIELD_ON(player))
7601       {
7602         player->shield_normal_time_left--;
7603
7604         if (player->shield_deadly_time_left > 0)
7605           player->shield_deadly_time_left--;
7606       }
7607     }
7608
7609     if (tape.recording || tape.playing)
7610       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7611
7612     if (TimeLeft > 0)
7613     {
7614       TimeLeft--;
7615
7616       if (TimeLeft <= 10 && setup.time_limit)
7617         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7618
7619       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7620
7621       if (!TimeLeft && setup.time_limit)
7622         for (i = 0; i < MAX_PLAYERS; i++)
7623           KillHero(&stored_player[i]);
7624     }
7625     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7626       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7627   }
7628
7629   DrawAllPlayers();
7630   PlayAllPlayersSound();
7631
7632   if (options.debug)                    /* calculate frames per second */
7633   {
7634     static unsigned long fps_counter = 0;
7635     static int fps_frames = 0;
7636     unsigned long fps_delay_ms = Counter() - fps_counter;
7637
7638     fps_frames++;
7639
7640     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7641     {
7642       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7643
7644       fps_frames = 0;
7645       fps_counter = Counter();
7646     }
7647
7648     redraw_mask |= REDRAW_FPS;
7649   }
7650
7651 #if 0
7652   if (stored_player[0].jx != stored_player[0].last_jx ||
7653       stored_player[0].jy != stored_player[0].last_jy)
7654     printf("::: %d, %d, %d, %d, %d\n",
7655            stored_player[0].MovDir,
7656            stored_player[0].MovPos,
7657            stored_player[0].GfxPos,
7658            stored_player[0].Frame,
7659            stored_player[0].StepFrame);
7660 #endif
7661
7662 #if 1
7663   FrameCounter++;
7664   TimeFrames++;
7665
7666   for (i = 0; i < MAX_PLAYERS; i++)
7667   {
7668     int move_frames =
7669       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
7670
7671     stored_player[i].Frame += move_frames;
7672
7673     if (stored_player[i].MovPos != 0)
7674       stored_player[i].StepFrame += move_frames;
7675
7676     if (stored_player[i].drop_delay > 0)
7677       stored_player[i].drop_delay--;
7678   }
7679 #endif
7680
7681 #if 1
7682   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7683   {
7684     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7685
7686     local_player->show_envelope = 0;
7687   }
7688 #endif
7689 }
7690
7691 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7692 {
7693   int min_x = x, min_y = y, max_x = x, max_y = y;
7694   int i;
7695
7696   for (i = 0; i < MAX_PLAYERS; i++)
7697   {
7698     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7699
7700     if (!stored_player[i].active || &stored_player[i] == player)
7701       continue;
7702
7703     min_x = MIN(min_x, jx);
7704     min_y = MIN(min_y, jy);
7705     max_x = MAX(max_x, jx);
7706     max_y = MAX(max_y, jy);
7707   }
7708
7709   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7710 }
7711
7712 static boolean AllPlayersInVisibleScreen()
7713 {
7714   int i;
7715
7716   for (i = 0; i < MAX_PLAYERS; i++)
7717   {
7718     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7719
7720     if (!stored_player[i].active)
7721       continue;
7722
7723     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7724       return FALSE;
7725   }
7726
7727   return TRUE;
7728 }
7729
7730 void ScrollLevel(int dx, int dy)
7731 {
7732   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7733   int x, y;
7734
7735   BlitBitmap(drawto_field, drawto_field,
7736              FX + TILEX * (dx == -1) - softscroll_offset,
7737              FY + TILEY * (dy == -1) - softscroll_offset,
7738              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7739              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7740              FX + TILEX * (dx == 1) - softscroll_offset,
7741              FY + TILEY * (dy == 1) - softscroll_offset);
7742
7743   if (dx)
7744   {
7745     x = (dx == 1 ? BX1 : BX2);
7746     for (y = BY1; y <= BY2; y++)
7747       DrawScreenField(x, y);
7748   }
7749
7750   if (dy)
7751   {
7752     y = (dy == 1 ? BY1 : BY2);
7753     for (x = BX1; x <= BX2; x++)
7754       DrawScreenField(x, y);
7755   }
7756
7757   redraw_mask |= REDRAW_FIELD;
7758 }
7759
7760 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7761 {
7762   int nextx = x + dx, nexty = y + dy;
7763   int element = Feld[x][y];
7764
7765   if ((dx == -1 &&
7766        element != EL_SP_PORT_LEFT &&
7767        element != EL_SP_GRAVITY_PORT_LEFT &&
7768        element != EL_SP_PORT_HORIZONTAL &&
7769        element != EL_SP_PORT_ANY) ||
7770       (dx == +1 &&
7771        element != EL_SP_PORT_RIGHT &&
7772        element != EL_SP_GRAVITY_PORT_RIGHT &&
7773        element != EL_SP_PORT_HORIZONTAL &&
7774        element != EL_SP_PORT_ANY) ||
7775       (dy == -1 &&
7776        element != EL_SP_PORT_UP &&
7777        element != EL_SP_GRAVITY_PORT_UP &&
7778        element != EL_SP_PORT_VERTICAL &&
7779        element != EL_SP_PORT_ANY) ||
7780       (dy == +1 &&
7781        element != EL_SP_PORT_DOWN &&
7782        element != EL_SP_GRAVITY_PORT_DOWN &&
7783        element != EL_SP_PORT_VERTICAL &&
7784        element != EL_SP_PORT_ANY) ||
7785       !IN_LEV_FIELD(nextx, nexty) ||
7786       !IS_FREE(nextx, nexty))
7787     return FALSE;
7788
7789   return TRUE;
7790 }
7791
7792 static void CheckGravityMovement(struct PlayerInfo *player)
7793 {
7794   if (game.gravity && !player->programmed_action)
7795   {
7796     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7797     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7798     int move_dir =
7799       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7800        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7801        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7802     int jx = player->jx, jy = player->jy;
7803     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7804     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7805     int new_jx = jx + dx, new_jy = jy + dy;
7806     boolean field_under_player_is_free =
7807       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7808     boolean player_is_moving_to_valid_field =
7809       (IN_LEV_FIELD(new_jx, new_jy) &&
7810        (Feld[new_jx][new_jy] == EL_SP_BASE ||
7811         Feld[new_jx][new_jy] == EL_SAND ||
7812         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7813          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7814     /* !!! extend EL_SAND to anything diggable !!! */
7815
7816     boolean player_is_standing_on_valid_field =
7817       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
7818        (IS_WALKABLE(Feld[jx][jy]) &&
7819         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
7820
7821     if (field_under_player_is_free &&
7822         !player_is_standing_on_valid_field &&
7823         !player_is_moving_to_valid_field)
7824       player->programmed_action = MV_DOWN;
7825   }
7826 }
7827
7828 /*
7829   MovePlayerOneStep()
7830   -----------------------------------------------------------------------------
7831   dx, dy:               direction (non-diagonal) to try to move the player to
7832   real_dx, real_dy:     direction as read from input device (can be diagonal)
7833 */
7834
7835 boolean MovePlayerOneStep(struct PlayerInfo *player,
7836                           int dx, int dy, int real_dx, int real_dy)
7837 {
7838 #if 0
7839   static int change_sides[4][2] =
7840   {
7841     /* enter side        leave side */
7842     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
7843     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
7844     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
7845     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
7846   };
7847   int move_direction = (dx == -1 ? MV_LEFT :
7848                         dx == +1 ? MV_RIGHT :
7849                         dy == -1 ? MV_UP :
7850                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7851   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7852   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7853 #endif
7854   int jx = player->jx, jy = player->jy;
7855   int new_jx = jx + dx, new_jy = jy + dy;
7856   int element;
7857   int can_move;
7858
7859   if (!player->active || (!dx && !dy))
7860     return MF_NO_ACTION;
7861
7862   player->MovDir = (dx < 0 ? MV_LEFT :
7863                     dx > 0 ? MV_RIGHT :
7864                     dy < 0 ? MV_UP :
7865                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
7866
7867   if (!IN_LEV_FIELD(new_jx, new_jy))
7868     return MF_NO_ACTION;
7869
7870   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7871     return MF_NO_ACTION;
7872
7873 #if 0
7874   element = MovingOrBlocked2Element(new_jx, new_jy);
7875 #else
7876   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7877 #endif
7878
7879   if (DONT_RUN_INTO(element))
7880   {
7881     if (element == EL_ACID && dx == 0 && dy == 1)
7882     {
7883       SplashAcid(jx, jy);
7884       Feld[jx][jy] = EL_PLAYER_1;
7885       InitMovingField(jx, jy, MV_DOWN);
7886       Store[jx][jy] = EL_ACID;
7887       ContinueMoving(jx, jy);
7888       BuryHero(player);
7889     }
7890     else
7891       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7892
7893     return MF_MOVING;
7894   }
7895
7896   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7897   if (can_move != MF_MOVING)
7898     return can_move;
7899
7900   /* check if DigField() has caused relocation of the player */
7901   if (player->jx != jx || player->jy != jy)
7902     return MF_NO_ACTION;
7903
7904   StorePlayer[jx][jy] = 0;
7905   player->last_jx = jx;
7906   player->last_jy = jy;
7907   player->jx = new_jx;
7908   player->jy = new_jy;
7909   StorePlayer[new_jx][new_jy] = player->element_nr;
7910
7911   player->MovPos =
7912     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7913
7914   player->step_counter++;
7915
7916   player->drop_delay = 0;
7917
7918   PlayerVisit[jx][jy] = FrameCounter;
7919
7920   ScrollPlayer(player, SCROLL_INIT);
7921
7922 #if 0
7923   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7924   {
7925     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7926                                     CE_OTHER_GETS_LEFT);
7927     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7928                            CE_LEFT_BY_PLAYER, -1);
7929   }
7930
7931   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7932   {
7933     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7934                                     enter_side, CE_OTHER_GETS_ENTERED);
7935     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7936                            CE_ENTERED_BY_PLAYER, -1);
7937   }
7938 #endif
7939
7940   return MF_MOVING;
7941 }
7942
7943 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7944 {
7945   int jx = player->jx, jy = player->jy;
7946   int old_jx = jx, old_jy = jy;
7947   int moved = MF_NO_ACTION;
7948
7949 #if 1
7950   if (!player->active)
7951     return FALSE;
7952
7953   if (!dx && !dy)
7954   {
7955     if (player->MovPos == 0)
7956     {
7957       player->is_moving = FALSE;
7958       player->is_digging = FALSE;
7959       player->is_collecting = FALSE;
7960       player->is_snapping = FALSE;
7961       player->is_pushing = FALSE;
7962     }
7963
7964     return FALSE;
7965   }
7966 #else
7967   if (!player->active || (!dx && !dy))
7968     return FALSE;
7969 #endif
7970
7971 #if 0
7972   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7973       !tape.playing)
7974     return FALSE;
7975 #else
7976   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7977       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7978     return FALSE;
7979 #endif
7980
7981   /* remove the last programmed player action */
7982   player->programmed_action = 0;
7983
7984   if (player->MovPos)
7985   {
7986     /* should only happen if pre-1.2 tape recordings are played */
7987     /* this is only for backward compatibility */
7988
7989     int original_move_delay_value = player->move_delay_value;
7990
7991 #if DEBUG
7992     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7993            tape.counter);
7994 #endif
7995
7996     /* scroll remaining steps with finest movement resolution */
7997     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7998
7999     while (player->MovPos)
8000     {
8001       ScrollPlayer(player, SCROLL_GO_ON);
8002       ScrollScreen(NULL, SCROLL_GO_ON);
8003       FrameCounter++;
8004       DrawAllPlayers();
8005       BackToFront();
8006     }
8007
8008     player->move_delay_value = original_move_delay_value;
8009   }
8010
8011   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
8012   {
8013     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8014       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8015   }
8016   else
8017   {
8018     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8019       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8020   }
8021
8022   jx = player->jx;
8023   jy = player->jy;
8024
8025   if (moved & MF_MOVING && !ScreenMovPos &&
8026       (player == local_player || !options.network))
8027   {
8028     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8029     int offset = (setup.scroll_delay ? 3 : 0);
8030
8031     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8032     {
8033       /* actual player has left the screen -- scroll in that direction */
8034       if (jx != old_jx)         /* player has moved horizontally */
8035         scroll_x += (jx - old_jx);
8036       else                      /* player has moved vertically */
8037         scroll_y += (jy - old_jy);
8038     }
8039     else
8040     {
8041       if (jx != old_jx)         /* player has moved horizontally */
8042       {
8043         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8044             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8045           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8046
8047         /* don't scroll over playfield boundaries */
8048         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8049           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8050
8051         /* don't scroll more than one field at a time */
8052         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8053
8054         /* don't scroll against the player's moving direction */
8055         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8056             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8057           scroll_x = old_scroll_x;
8058       }
8059       else                      /* player has moved vertically */
8060       {
8061         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8062             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8063           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8064
8065         /* don't scroll over playfield boundaries */
8066         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8067           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8068
8069         /* don't scroll more than one field at a time */
8070         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8071
8072         /* don't scroll against the player's moving direction */
8073         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8074             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8075           scroll_y = old_scroll_y;
8076       }
8077     }
8078
8079     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8080     {
8081       if (!options.network && !AllPlayersInVisibleScreen())
8082       {
8083         scroll_x = old_scroll_x;
8084         scroll_y = old_scroll_y;
8085       }
8086       else
8087       {
8088         ScrollScreen(player, SCROLL_INIT);
8089         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8090       }
8091     }
8092   }
8093
8094 #if 0
8095 #if 1
8096   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8097 #else
8098   if (!(moved & MF_MOVING) && !player->is_pushing)
8099     player->Frame = 0;
8100 #endif
8101 #endif
8102
8103   player->StepFrame = 0;
8104
8105   if (moved & MF_MOVING)
8106   {
8107     if (old_jx != jx && old_jy == jy)
8108       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8109     else if (old_jx == jx && old_jy != jy)
8110       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8111
8112     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8113
8114     player->last_move_dir = player->MovDir;
8115     player->is_moving = TRUE;
8116 #if 1
8117     player->is_snapping = FALSE;
8118 #endif
8119
8120 #if 1
8121     player->is_switching = FALSE;
8122 #endif
8123
8124     player->is_dropping = FALSE;
8125
8126
8127 #if 1
8128     {
8129       static int change_sides[4][2] =
8130       {
8131         /* enter side           leave side */
8132         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8133         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8134         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8135         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8136       };
8137       int move_direction = player->MovDir;
8138       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8139       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8140
8141 #if 1
8142       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8143       {
8144         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8145                                         leave_side, CE_OTHER_GETS_LEFT);
8146         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8147                                leave_side, CE_LEFT_BY_PLAYER, -1);
8148       }
8149
8150       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8151       {
8152         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8153                                         enter_side, CE_OTHER_GETS_ENTERED);
8154         CheckElementSideChange(jx, jy, Feld[jx][jy],
8155                                enter_side, CE_ENTERED_BY_PLAYER, -1);
8156       }
8157 #endif
8158
8159     }
8160 #endif
8161
8162
8163   }
8164   else
8165   {
8166     CheckGravityMovement(player);
8167
8168     /*
8169     player->last_move_dir = MV_NO_MOVING;
8170     */
8171     player->is_moving = FALSE;
8172   }
8173
8174   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8175   {
8176     TestIfHeroTouchesBadThing(jx, jy);
8177     TestIfPlayerTouchesCustomElement(jx, jy);
8178   }
8179
8180   if (!player->active)
8181     RemoveHero(player);
8182
8183   return moved;
8184 }
8185
8186 void ScrollPlayer(struct PlayerInfo *player, int mode)
8187 {
8188   int jx = player->jx, jy = player->jy;
8189   int last_jx = player->last_jx, last_jy = player->last_jy;
8190   int move_stepsize = TILEX / player->move_delay_value;
8191
8192   if (!player->active || !player->MovPos)
8193     return;
8194
8195   if (mode == SCROLL_INIT)
8196   {
8197     player->actual_frame_counter = FrameCounter;
8198     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8199
8200     if (Feld[last_jx][last_jy] == EL_EMPTY)
8201       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8202
8203 #if 0
8204     DrawPlayer(player);
8205 #endif
8206
8207     return;
8208   }
8209   else if (!FrameReached(&player->actual_frame_counter, 1))
8210     return;
8211
8212   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8213   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8214
8215   if (!player->block_last_field &&
8216       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8217     Feld[last_jx][last_jy] = EL_EMPTY;
8218
8219   /* before DrawPlayer() to draw correct player graphic for this case */
8220   if (player->MovPos == 0)
8221     CheckGravityMovement(player);
8222
8223 #if 0
8224   DrawPlayer(player);   /* needed here only to cleanup last field */
8225 #endif
8226
8227   if (player->MovPos == 0)      /* player reached destination field */
8228   {
8229 #if 1
8230     if (player->move_delay_reset_counter > 0)
8231     {
8232       player->move_delay_reset_counter--;
8233
8234       if (player->move_delay_reset_counter == 0)
8235       {
8236         /* continue with normal speed after quickly moving through gate */
8237         HALVE_PLAYER_SPEED(player);
8238
8239         /* be able to make the next move without delay */
8240         player->move_delay = 0;
8241       }
8242     }
8243 #else
8244     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8245     {
8246       /* continue with normal speed after quickly moving through gate */
8247       HALVE_PLAYER_SPEED(player);
8248
8249       /* be able to make the next move without delay */
8250       player->move_delay = 0;
8251     }
8252 #endif
8253
8254     if (player->block_last_field &&
8255         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8256       Feld[last_jx][last_jy] = EL_EMPTY;
8257
8258     player->last_jx = jx;
8259     player->last_jy = jy;
8260
8261     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8262         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8263         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8264     {
8265       DrawPlayer(player);       /* needed here only to cleanup last field */
8266       RemoveHero(player);
8267
8268       if (local_player->friends_still_needed == 0 ||
8269           IS_SP_ELEMENT(Feld[jx][jy]))
8270         player->LevelSolved = player->GameOver = TRUE;
8271     }
8272
8273     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8274     {
8275       TestIfHeroTouchesBadThing(jx, jy);
8276       TestIfPlayerTouchesCustomElement(jx, jy);
8277 #if 1
8278       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8279 #endif
8280
8281       if (!player->active)
8282         RemoveHero(player);
8283     }
8284
8285     if (tape.single_step && tape.recording && !tape.pausing &&
8286         !player->programmed_action)
8287       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8288   }
8289 }
8290
8291 void ScrollScreen(struct PlayerInfo *player, int mode)
8292 {
8293   static unsigned long screen_frame_counter = 0;
8294
8295   if (mode == SCROLL_INIT)
8296   {
8297     /* set scrolling step size according to actual player's moving speed */
8298     ScrollStepSize = TILEX / player->move_delay_value;
8299
8300     screen_frame_counter = FrameCounter;
8301     ScreenMovDir = player->MovDir;
8302     ScreenMovPos = player->MovPos;
8303     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8304     return;
8305   }
8306   else if (!FrameReached(&screen_frame_counter, 1))
8307     return;
8308
8309   if (ScreenMovPos)
8310   {
8311     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8312     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8313     redraw_mask |= REDRAW_FIELD;
8314   }
8315   else
8316     ScreenMovDir = MV_NO_MOVING;
8317 }
8318
8319 void TestIfPlayerTouchesCustomElement(int x, int y)
8320 {
8321   static int xy[4][2] =
8322   {
8323     { 0, -1 },
8324     { -1, 0 },
8325     { +1, 0 },
8326     { 0, +1 }
8327   };
8328   static int change_sides[4][2] =
8329   {
8330     /* center side       border side */
8331     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8332     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8333     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8334     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8335   };
8336   static int touch_dir[4] =
8337   {
8338     MV_LEFT | MV_RIGHT,
8339     MV_UP   | MV_DOWN,
8340     MV_UP   | MV_DOWN,
8341     MV_LEFT | MV_RIGHT
8342   };
8343   int center_element = Feld[x][y];      /* should always be non-moving! */
8344   int i;
8345
8346   for (i = 0; i < 4; i++)
8347   {
8348     int xx = x + xy[i][0];
8349     int yy = y + xy[i][1];
8350     int center_side = change_sides[i][0];
8351     int border_side = change_sides[i][1];
8352     int border_element;
8353
8354     if (!IN_LEV_FIELD(xx, yy))
8355       continue;
8356
8357     if (IS_PLAYER(x, y))
8358     {
8359       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8360         border_element = Feld[xx][yy];          /* may be moving! */
8361       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8362         border_element = Feld[xx][yy];
8363       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8364         border_element = MovingOrBlocked2Element(xx, yy);
8365       else
8366         continue;               /* center and border element do not touch */
8367
8368       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8369                                       CE_OTHER_GETS_TOUCHED);
8370       CheckElementSideChange(xx, yy, border_element, border_side,
8371                              CE_TOUCHED_BY_PLAYER, -1);
8372     }
8373     else if (IS_PLAYER(xx, yy))
8374     {
8375       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8376       {
8377         struct PlayerInfo *player = PLAYERINFO(xx, yy);
8378
8379         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8380           continue;             /* center and border element do not touch */
8381       }
8382
8383       CheckTriggeredElementSideChange(x, y, center_element, center_side,
8384                                       CE_OTHER_GETS_TOUCHED);
8385       CheckElementSideChange(x, y, center_element, center_side,
8386                              CE_TOUCHED_BY_PLAYER, -1);
8387
8388       break;
8389     }
8390   }
8391 }
8392
8393 void TestIfElementTouchesCustomElement(int x, int y)
8394 {
8395   static int xy[4][2] =
8396   {
8397     { 0, -1 },
8398     { -1, 0 },
8399     { +1, 0 },
8400     { 0, +1 }
8401   };
8402   static int change_sides[4][2] =
8403   {
8404     /* center side      border side */
8405     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8406     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8407     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8408     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8409   };
8410   static int touch_dir[4] =
8411   {
8412     MV_LEFT | MV_RIGHT,
8413     MV_UP   | MV_DOWN,
8414     MV_UP   | MV_DOWN,
8415     MV_LEFT | MV_RIGHT
8416   };
8417   boolean change_center_element = FALSE;
8418   int center_element_change_page = 0;
8419   int center_element = Feld[x][y];      /* should always be non-moving! */
8420   int i, j;
8421
8422   for (i = 0; i < 4; i++)
8423   {
8424     int xx = x + xy[i][0];
8425     int yy = y + xy[i][1];
8426     int center_side = change_sides[i][0];
8427     int border_side = change_sides[i][1];
8428     int border_element;
8429
8430     if (!IN_LEV_FIELD(xx, yy))
8431       continue;
8432
8433     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8434       border_element = Feld[xx][yy];    /* may be moving! */
8435     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8436       border_element = Feld[xx][yy];
8437     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8438       border_element = MovingOrBlocked2Element(xx, yy);
8439     else
8440       continue;                 /* center and border element do not touch */
8441
8442     /* check for change of center element (but change it only once) */
8443     if (IS_CUSTOM_ELEMENT(center_element) &&
8444         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8445         !change_center_element)
8446     {
8447       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8448       {
8449         struct ElementChangeInfo *change =
8450           &element_info[center_element].change_page[j];
8451
8452         if (change->can_change &&
8453             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8454             change->sides & border_side &&
8455 #if 1
8456             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8457 #else
8458             change->trigger_element == border_element
8459 #endif
8460             )
8461         {
8462           change_center_element = TRUE;
8463           center_element_change_page = j;
8464
8465           break;
8466         }
8467       }
8468     }
8469
8470     /* check for change of border element */
8471     if (IS_CUSTOM_ELEMENT(border_element) &&
8472         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8473     {
8474       for (j = 0; j < element_info[border_element].num_change_pages; j++)
8475       {
8476         struct ElementChangeInfo *change =
8477           &element_info[border_element].change_page[j];
8478
8479         if (change->can_change &&
8480             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8481             change->sides & center_side &&
8482 #if 1
8483             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8484 #else
8485             change->trigger_element == center_element
8486 #endif
8487             )
8488         {
8489           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8490                                  CE_OTHER_IS_TOUCHING, j);
8491           break;
8492         }
8493       }
8494     }
8495   }
8496
8497   if (change_center_element)
8498     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8499                            CE_OTHER_IS_TOUCHING, center_element_change_page);
8500 }
8501
8502 void TestIfElementHitsCustomElement(int x, int y, int direction)
8503 {
8504   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8505   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8506   int hitx = x + dx, hity = y + dy;
8507   int hitting_element = Feld[x][y];
8508 #if 0
8509   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8510                         !IS_FREE(hitx, hity) &&
8511                         (!IS_MOVING(hitx, hity) ||
8512                          MovDir[hitx][hity] != direction ||
8513                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
8514 #endif
8515
8516   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8517     return;
8518
8519 #if 0
8520   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8521     return;
8522 #endif
8523
8524   CheckElementSideChange(x, y, hitting_element,
8525                          direction, CE_HITTING_SOMETHING, -1);
8526
8527   if (IN_LEV_FIELD(hitx, hity))
8528   {
8529     int opposite_direction = MV_DIR_OPPOSITE(direction);
8530     int hitting_side = direction;
8531     int touched_side = opposite_direction;
8532     int touched_element = MovingOrBlocked2Element(hitx, hity);
8533 #if 1
8534     boolean object_hit = (!IS_MOVING(hitx, hity) ||
8535                           MovDir[hitx][hity] != direction ||
8536                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
8537
8538     object_hit = TRUE;
8539 #endif
8540
8541     if (object_hit)
8542     {
8543       int i;
8544
8545       CheckElementSideChange(hitx, hity, touched_element,
8546                              opposite_direction, CE_HIT_BY_SOMETHING, -1);
8547
8548       if (IS_CUSTOM_ELEMENT(hitting_element) &&
8549           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8550       {
8551         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8552         {
8553           struct ElementChangeInfo *change =
8554             &element_info[hitting_element].change_page[i];
8555
8556           if (change->can_change &&
8557               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8558               change->sides & touched_side &&
8559           
8560 #if 1
8561               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8562 #else
8563               change->trigger_element == touched_element
8564 #endif
8565               )
8566           {
8567             CheckElementSideChange(x, y, hitting_element,
8568                                    CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8569             break;
8570           }
8571         }
8572       }
8573
8574       if (IS_CUSTOM_ELEMENT(touched_element) &&
8575           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8576       {
8577         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8578         {
8579           struct ElementChangeInfo *change =
8580             &element_info[touched_element].change_page[i];
8581
8582           if (change->can_change &&
8583               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8584               change->sides & hitting_side &&
8585 #if 1
8586               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8587 #else
8588               change->trigger_element == hitting_element
8589 #endif
8590               )
8591           {
8592             CheckElementSideChange(hitx, hity, touched_element,
8593                                    CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8594             break;
8595           }
8596         }
8597       }
8598     }
8599   }
8600 }
8601
8602 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8603 {
8604   int i, kill_x = -1, kill_y = -1;
8605   static int test_xy[4][2] =
8606   {
8607     { 0, -1 },
8608     { -1, 0 },
8609     { +1, 0 },
8610     { 0, +1 }
8611   };
8612   static int test_dir[4] =
8613   {
8614     MV_UP,
8615     MV_LEFT,
8616     MV_RIGHT,
8617     MV_DOWN
8618   };
8619
8620   for (i = 0; i < 4; i++)
8621   {
8622     int test_x, test_y, test_move_dir, test_element;
8623
8624     test_x = good_x + test_xy[i][0];
8625     test_y = good_y + test_xy[i][1];
8626     if (!IN_LEV_FIELD(test_x, test_y))
8627       continue;
8628
8629     test_move_dir =
8630       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8631
8632 #if 0
8633     test_element = Feld[test_x][test_y];
8634 #else
8635     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8636 #endif
8637
8638     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8639        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8640     */
8641     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8642         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
8643     {
8644       kill_x = test_x;
8645       kill_y = test_y;
8646       break;
8647     }
8648   }
8649
8650   if (kill_x != -1 || kill_y != -1)
8651   {
8652     if (IS_PLAYER(good_x, good_y))
8653     {
8654       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8655
8656       if (player->shield_deadly_time_left > 0)
8657         Bang(kill_x, kill_y);
8658       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8659         KillHero(player);
8660     }
8661     else
8662       Bang(good_x, good_y);
8663   }
8664 }
8665
8666 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8667 {
8668   int i, kill_x = -1, kill_y = -1;
8669   int bad_element = Feld[bad_x][bad_y];
8670   static int test_xy[4][2] =
8671   {
8672     { 0, -1 },
8673     { -1, 0 },
8674     { +1, 0 },
8675     { 0, +1 }
8676   };
8677   static int touch_dir[4] =
8678   {
8679     MV_LEFT | MV_RIGHT,
8680     MV_UP   | MV_DOWN,
8681     MV_UP   | MV_DOWN,
8682     MV_LEFT | MV_RIGHT
8683   };
8684   static int test_dir[4] =
8685   {
8686     MV_UP,
8687     MV_LEFT,
8688     MV_RIGHT,
8689     MV_DOWN
8690   };
8691
8692   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
8693     return;
8694
8695   for (i = 0; i < 4; i++)
8696   {
8697     int test_x, test_y, test_move_dir, test_element;
8698
8699     test_x = bad_x + test_xy[i][0];
8700     test_y = bad_y + test_xy[i][1];
8701     if (!IN_LEV_FIELD(test_x, test_y))
8702       continue;
8703
8704     test_move_dir =
8705       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8706
8707     test_element = Feld[test_x][test_y];
8708
8709     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8710        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8711     */
8712     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
8713         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
8714     {
8715       /* good thing is player or penguin that does not move away */
8716       if (IS_PLAYER(test_x, test_y))
8717       {
8718         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8719
8720         if (bad_element == EL_ROBOT && player->is_moving)
8721           continue;     /* robot does not kill player if he is moving */
8722
8723         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8724         {
8725           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8726             continue;           /* center and border element do not touch */
8727         }
8728
8729         kill_x = test_x;
8730         kill_y = test_y;
8731         break;
8732       }
8733       else if (test_element == EL_PENGUIN)
8734       {
8735         kill_x = test_x;
8736         kill_y = test_y;
8737         break;
8738       }
8739     }
8740   }
8741
8742   if (kill_x != -1 || kill_y != -1)
8743   {
8744     if (IS_PLAYER(kill_x, kill_y))
8745     {
8746       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8747
8748       if (player->shield_deadly_time_left > 0)
8749         Bang(bad_x, bad_y);
8750       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8751         KillHero(player);
8752     }
8753     else
8754       Bang(kill_x, kill_y);
8755   }
8756 }
8757
8758 void TestIfHeroTouchesBadThing(int x, int y)
8759 {
8760   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8761 }
8762
8763 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8764 {
8765   TestIfGoodThingHitsBadThing(x, y, move_dir);
8766 }
8767
8768 void TestIfBadThingTouchesHero(int x, int y)
8769 {
8770   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8771 }
8772
8773 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8774 {
8775   TestIfBadThingHitsGoodThing(x, y, move_dir);
8776 }
8777
8778 void TestIfFriendTouchesBadThing(int x, int y)
8779 {
8780   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8781 }
8782
8783 void TestIfBadThingTouchesFriend(int x, int y)
8784 {
8785   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8786 }
8787
8788 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8789 {
8790   int i, kill_x = bad_x, kill_y = bad_y;
8791   static int xy[4][2] =
8792   {
8793     { 0, -1 },
8794     { -1, 0 },
8795     { +1, 0 },
8796     { 0, +1 }
8797   };
8798
8799   for (i = 0; i < 4; i++)
8800   {
8801     int x, y, element;
8802
8803     x = bad_x + xy[i][0];
8804     y = bad_y + xy[i][1];
8805     if (!IN_LEV_FIELD(x, y))
8806       continue;
8807
8808     element = Feld[x][y];
8809     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8810         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8811     {
8812       kill_x = x;
8813       kill_y = y;
8814       break;
8815     }
8816   }
8817
8818   if (kill_x != bad_x || kill_y != bad_y)
8819     Bang(bad_x, bad_y);
8820 }
8821
8822 void KillHero(struct PlayerInfo *player)
8823 {
8824   int jx = player->jx, jy = player->jy;
8825
8826   if (!player->active)
8827     return;
8828
8829   /* remove accessible field at the player's position */
8830   Feld[jx][jy] = EL_EMPTY;
8831
8832   /* deactivate shield (else Bang()/Explode() would not work right) */
8833   player->shield_normal_time_left = 0;
8834   player->shield_deadly_time_left = 0;
8835
8836   Bang(jx, jy);
8837   BuryHero(player);
8838 }
8839
8840 static void KillHeroUnlessEnemyProtected(int x, int y)
8841 {
8842   if (!PLAYER_ENEMY_PROTECTED(x, y))
8843     KillHero(PLAYERINFO(x, y));
8844 }
8845
8846 static void KillHeroUnlessExplosionProtected(int x, int y)
8847 {
8848   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8849     KillHero(PLAYERINFO(x, y));
8850 }
8851
8852 void BuryHero(struct PlayerInfo *player)
8853 {
8854   int jx = player->jx, jy = player->jy;
8855
8856   if (!player->active)
8857     return;
8858
8859 #if 1
8860   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8861 #else
8862   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8863 #endif
8864   PlayLevelSound(jx, jy, SND_GAME_LOSING);
8865
8866   player->GameOver = TRUE;
8867   RemoveHero(player);
8868 }
8869
8870 void RemoveHero(struct PlayerInfo *player)
8871 {
8872   int jx = player->jx, jy = player->jy;
8873   int i, found = FALSE;
8874
8875   player->present = FALSE;
8876   player->active = FALSE;
8877
8878   if (!ExplodeField[jx][jy])
8879     StorePlayer[jx][jy] = 0;
8880
8881   for (i = 0; i < MAX_PLAYERS; i++)
8882     if (stored_player[i].active)
8883       found = TRUE;
8884
8885   if (!found)
8886     AllPlayersGone = TRUE;
8887
8888   ExitX = ZX = jx;
8889   ExitY = ZY = jy;
8890 }
8891
8892 /*
8893   =============================================================================
8894   checkDiagonalPushing()
8895   -----------------------------------------------------------------------------
8896   check if diagonal input device direction results in pushing of object
8897   (by checking if the alternative direction is walkable, diggable, ...)
8898   =============================================================================
8899 */
8900
8901 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8902                                     int x, int y, int real_dx, int real_dy)
8903 {
8904   int jx, jy, dx, dy, xx, yy;
8905
8906   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
8907     return TRUE;
8908
8909   /* diagonal direction: check alternative direction */
8910   jx = player->jx;
8911   jy = player->jy;
8912   dx = x - jx;
8913   dy = y - jy;
8914   xx = jx + (dx == 0 ? real_dx : 0);
8915   yy = jy + (dy == 0 ? real_dy : 0);
8916
8917   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8918 }
8919
8920 /*
8921   =============================================================================
8922   DigField()
8923   -----------------------------------------------------------------------------
8924   x, y:                 field next to player (non-diagonal) to try to dig to
8925   real_dx, real_dy:     direction as read from input device (can be diagonal)
8926   =============================================================================
8927 */
8928
8929 int DigField(struct PlayerInfo *player,
8930              int x, int y, int real_dx, int real_dy, int mode)
8931 {
8932   static int change_sides[4] =
8933   {
8934     CH_SIDE_RIGHT,      /* moving left  */
8935     CH_SIDE_LEFT,       /* moving right */
8936     CH_SIDE_BOTTOM,     /* moving up    */
8937     CH_SIDE_TOP,        /* moving down  */
8938   };
8939   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8940   int jx = player->jx, jy = player->jy;
8941   int dx = x - jx, dy = y - jy;
8942   int nextx = x + dx, nexty = y + dy;
8943   int move_direction = (dx == -1 ? MV_LEFT :
8944                         dx == +1 ? MV_RIGHT :
8945                         dy == -1 ? MV_UP :
8946                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8947   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
8948   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8949   int old_element = Feld[jx][jy];
8950   int element;
8951
8952   if (player->MovPos == 0)
8953   {
8954     player->is_digging = FALSE;
8955     player->is_collecting = FALSE;
8956   }
8957
8958   if (player->MovPos == 0)      /* last pushing move finished */
8959     player->is_pushing = FALSE;
8960
8961   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
8962   {
8963     player->is_switching = FALSE;
8964     player->push_delay = 0;
8965
8966     return MF_NO_ACTION;
8967   }
8968
8969   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8970     return MF_NO_ACTION;
8971
8972 #if 0
8973
8974 #if 0
8975   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8976 #else
8977   if (IS_TUBE(Feld[jx][jy]) ||
8978       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8979 #endif
8980   {
8981     int i = 0;
8982     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8983     int tube_leave_directions[][2] =
8984     {
8985       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8986       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
8987       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
8988       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
8989       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
8990       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
8991       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
8992       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
8993       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
8994       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
8995       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
8996       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8997     };
8998
8999     while (tube_leave_directions[i][0] != tube_element)
9000     {
9001       i++;
9002       if (tube_leave_directions[i][0] == -1)    /* should not happen */
9003         break;
9004     }
9005
9006     if (!(tube_leave_directions[i][1] & move_direction))
9007       return MF_NO_ACTION;      /* tube has no opening in this direction */
9008   }
9009
9010 #else
9011
9012   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9013     old_element = Back[jx][jy];
9014
9015 #endif
9016
9017   if (IS_WALKABLE(old_element) &&
9018       !(element_info[old_element].access_direction & move_direction))
9019     return MF_NO_ACTION;        /* field has no opening in this direction */
9020
9021   element = Feld[x][y];
9022
9023   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9024       game.engine_version >= VERSION_IDENT(2,2,0,0))
9025     return MF_NO_ACTION;
9026
9027   switch (element)
9028   {
9029     case EL_SP_PORT_LEFT:
9030     case EL_SP_PORT_RIGHT:
9031     case EL_SP_PORT_UP:
9032     case EL_SP_PORT_DOWN:
9033     case EL_SP_PORT_HORIZONTAL:
9034     case EL_SP_PORT_VERTICAL:
9035     case EL_SP_PORT_ANY:
9036     case EL_SP_GRAVITY_PORT_LEFT:
9037     case EL_SP_GRAVITY_PORT_RIGHT:
9038     case EL_SP_GRAVITY_PORT_UP:
9039     case EL_SP_GRAVITY_PORT_DOWN:
9040 #if 1
9041       if (!canEnterSupaplexPort(x, y, dx, dy))
9042         return MF_NO_ACTION;
9043 #else
9044       if ((dx == -1 &&
9045            element != EL_SP_PORT_LEFT &&
9046            element != EL_SP_GRAVITY_PORT_LEFT &&
9047            element != EL_SP_PORT_HORIZONTAL &&
9048            element != EL_SP_PORT_ANY) ||
9049           (dx == +1 &&
9050            element != EL_SP_PORT_RIGHT &&
9051            element != EL_SP_GRAVITY_PORT_RIGHT &&
9052            element != EL_SP_PORT_HORIZONTAL &&
9053            element != EL_SP_PORT_ANY) ||
9054           (dy == -1 &&
9055            element != EL_SP_PORT_UP &&
9056            element != EL_SP_GRAVITY_PORT_UP &&
9057            element != EL_SP_PORT_VERTICAL &&
9058            element != EL_SP_PORT_ANY) ||
9059           (dy == +1 &&
9060            element != EL_SP_PORT_DOWN &&
9061            element != EL_SP_GRAVITY_PORT_DOWN &&
9062            element != EL_SP_PORT_VERTICAL &&
9063            element != EL_SP_PORT_ANY) ||
9064           !IN_LEV_FIELD(nextx, nexty) ||
9065           !IS_FREE(nextx, nexty))
9066         return MF_NO_ACTION;
9067 #endif
9068
9069       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9070           element == EL_SP_GRAVITY_PORT_RIGHT ||
9071           element == EL_SP_GRAVITY_PORT_UP ||
9072           element == EL_SP_GRAVITY_PORT_DOWN)
9073         game.gravity = !game.gravity;
9074
9075       /* automatically move to the next field with double speed */
9076       player->programmed_action = move_direction;
9077 #if 1
9078       if (player->move_delay_reset_counter == 0)
9079       {
9080         player->move_delay_reset_counter = 2;   /* two double speed steps */
9081
9082         DOUBLE_PLAYER_SPEED(player);
9083       }
9084 #else
9085       player->move_delay_reset_counter = 2;
9086
9087       DOUBLE_PLAYER_SPEED(player);
9088 #endif
9089
9090       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9091       break;
9092
9093 #if 0
9094     case EL_TUBE_ANY:
9095     case EL_TUBE_VERTICAL:
9096     case EL_TUBE_HORIZONTAL:
9097     case EL_TUBE_VERTICAL_LEFT:
9098     case EL_TUBE_VERTICAL_RIGHT:
9099     case EL_TUBE_HORIZONTAL_UP:
9100     case EL_TUBE_HORIZONTAL_DOWN:
9101     case EL_TUBE_LEFT_UP:
9102     case EL_TUBE_LEFT_DOWN:
9103     case EL_TUBE_RIGHT_UP:
9104     case EL_TUBE_RIGHT_DOWN:
9105       {
9106         int i = 0;
9107         int tube_enter_directions[][2] =
9108         {
9109           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9110           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
9111           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
9112           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
9113           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
9114           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
9115           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
9116           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
9117           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
9118           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
9119           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
9120           { -1,                         MV_NO_MOVING                         }
9121         };
9122
9123         while (tube_enter_directions[i][0] != element)
9124         {
9125           i++;
9126           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9127             break;
9128         }
9129
9130         if (!(tube_enter_directions[i][1] & move_direction))
9131           return MF_NO_ACTION;  /* tube has no opening in this direction */
9132
9133         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9134       }
9135       break;
9136 #endif
9137
9138     default:
9139
9140       if (IS_WALKABLE(element))
9141       {
9142         int sound_action = ACTION_WALKING;
9143
9144         if (!(element_info[element].access_direction & opposite_direction))
9145           return MF_NO_ACTION;  /* field not accessible from this direction */
9146
9147         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9148         {
9149           if (!player->key[element - EL_GATE_1])
9150             return MF_NO_ACTION;
9151         }
9152         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9153         {
9154           if (!player->key[element - EL_GATE_1_GRAY])
9155             return MF_NO_ACTION;
9156         }
9157         else if (element == EL_EXIT_OPEN ||
9158                  element == EL_SP_EXIT_OPEN ||
9159                  element == EL_SP_EXIT_OPENING)
9160         {
9161           sound_action = ACTION_PASSING;        /* player is passing exit */
9162         }
9163         else if (element == EL_EMPTY)
9164         {
9165           sound_action = ACTION_MOVING;         /* nothing to walk on */
9166         }
9167
9168         /* play sound from background or player, whatever is available */
9169         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9170           PlayLevelSoundElementAction(x, y, element, sound_action);
9171         else
9172           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9173
9174         break;
9175       }
9176       else if (IS_PASSABLE(element))
9177       {
9178         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9179           return MF_NO_ACTION;
9180
9181         if (IS_CUSTOM_ELEMENT(element) &&
9182             !(element_info[element].access_direction & opposite_direction))
9183           return MF_NO_ACTION;  /* field not accessible from this direction */
9184
9185 #if 1
9186         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9187           return MF_NO_ACTION;
9188 #endif
9189
9190         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9191         {
9192           if (!player->key[element - EL_EM_GATE_1])
9193             return MF_NO_ACTION;
9194         }
9195         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9196         {
9197           if (!player->key[element - EL_EM_GATE_1_GRAY])
9198             return MF_NO_ACTION;
9199         }
9200
9201         /* automatically move to the next field with double speed */
9202         player->programmed_action = move_direction;
9203 #if 1
9204         if (player->move_delay_reset_counter == 0)
9205         {
9206           player->move_delay_reset_counter = 2; /* two double speed steps */
9207
9208           DOUBLE_PLAYER_SPEED(player);
9209         }
9210 #else
9211         player->move_delay_reset_counter = 2;
9212
9213         DOUBLE_PLAYER_SPEED(player);
9214 #endif
9215
9216         PlayLevelSoundAction(x, y, ACTION_PASSING);
9217
9218         break;
9219       }
9220       else if (IS_DIGGABLE(element))
9221       {
9222         RemoveField(x, y);
9223
9224         if (mode != DF_SNAP)
9225         {
9226 #if 1
9227           GfxElement[x][y] = GFX_ELEMENT(element);
9228 #else
9229           GfxElement[x][y] =
9230             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9231 #endif
9232           player->is_digging = TRUE;
9233         }
9234
9235         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9236
9237         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9238
9239 #if 1
9240         if (mode == DF_SNAP)
9241           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9242 #endif
9243
9244         break;
9245       }
9246       else if (IS_COLLECTIBLE(element))
9247       {
9248         RemoveField(x, y);
9249
9250         if (mode != DF_SNAP)
9251         {
9252           GfxElement[x][y] = element;
9253           player->is_collecting = TRUE;
9254         }
9255
9256         if (element == EL_SPEED_PILL)
9257           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9258         else if (element == EL_EXTRA_TIME && level.time > 0)
9259         {
9260           TimeLeft += 10;
9261           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9262         }
9263         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9264         {
9265           player->shield_normal_time_left += 10;
9266           if (element == EL_SHIELD_DEADLY)
9267             player->shield_deadly_time_left += 10;
9268         }
9269         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9270         {
9271           if (player->inventory_size < MAX_INVENTORY_SIZE)
9272             player->inventory_element[player->inventory_size++] = element;
9273
9274           DrawText(DX_DYNAMITE, DY_DYNAMITE,
9275                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9276         }
9277         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9278         {
9279           player->dynabomb_count++;
9280           player->dynabombs_left++;
9281         }
9282         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9283         {
9284           player->dynabomb_size++;
9285         }
9286         else if (element == EL_DYNABOMB_INCREASE_POWER)
9287         {
9288           player->dynabomb_xl = TRUE;
9289         }
9290         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9291                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9292         {
9293           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9294                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9295
9296           player->key[key_nr] = TRUE;
9297
9298           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9299                              el2edimg(EL_KEY_1 + key_nr));
9300           redraw_mask |= REDRAW_DOOR_1;
9301         }
9302         else if (IS_ENVELOPE(element))
9303         {
9304 #if 1
9305           player->show_envelope = element;
9306 #else
9307           ShowEnvelope(element - EL_ENVELOPE_1);
9308 #endif
9309         }
9310         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9311         {
9312           int i;
9313
9314           for (i = 0; i < element_info[element].collect_count; i++)
9315             if (player->inventory_size < MAX_INVENTORY_SIZE)
9316               player->inventory_element[player->inventory_size++] = element;
9317
9318           DrawText(DX_DYNAMITE, DY_DYNAMITE,
9319                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9320         }
9321         else if (element_info[element].collect_count > 0)
9322         {
9323           local_player->gems_still_needed -=
9324             element_info[element].collect_count;
9325           if (local_player->gems_still_needed < 0)
9326             local_player->gems_still_needed = 0;
9327
9328           DrawText(DX_EMERALDS, DY_EMERALDS,
9329                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9330         }
9331
9332         RaiseScoreElement(element);
9333         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9334
9335         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9336
9337 #if 1
9338         if (mode == DF_SNAP)
9339           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9340 #endif
9341
9342         break;
9343       }
9344       else if (IS_PUSHABLE(element))
9345       {
9346         if (mode == DF_SNAP && element != EL_BD_ROCK)
9347           return MF_NO_ACTION;
9348
9349         if (CAN_FALL(element) && dy)
9350           return MF_NO_ACTION;
9351
9352         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9353             !(element == EL_SPRING && use_spring_bug))
9354           return MF_NO_ACTION;
9355
9356 #if 1
9357         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9358             ((move_direction & MV_VERTICAL &&
9359               ((element_info[element].move_pattern & MV_LEFT &&
9360                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9361                (element_info[element].move_pattern & MV_RIGHT &&
9362                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9363              (move_direction & MV_HORIZONTAL &&
9364               ((element_info[element].move_pattern & MV_UP &&
9365                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9366                (element_info[element].move_pattern & MV_DOWN &&
9367                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9368           return MF_NO_ACTION;
9369 #endif
9370
9371 #if 1
9372         /* do not push elements already moving away faster than player */
9373         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9374             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9375           return MF_NO_ACTION;
9376 #else
9377         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9378           return MF_NO_ACTION;
9379 #endif
9380
9381 #if 1
9382         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9383         {
9384           if (player->push_delay_value == -1)
9385             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9386         }
9387         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9388         {
9389           if (!player->is_pushing)
9390             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9391         }
9392
9393         /*
9394         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9395             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9396              !player_is_pushing))
9397           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9398         */
9399 #else
9400         if (!player->is_pushing &&
9401             game.engine_version >= VERSION_IDENT(2,2,0,7))
9402           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9403 #endif
9404
9405 #if 0
9406         printf("::: push delay: %ld [%d, %d] [%d]\n",
9407                player->push_delay_value, FrameCounter, game.engine_version,
9408                player->is_pushing);
9409 #endif
9410
9411         player->is_pushing = TRUE;
9412
9413         if (!(IN_LEV_FIELD(nextx, nexty) &&
9414               (IS_FREE(nextx, nexty) ||
9415                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9416                 IS_SB_ELEMENT(element)))))
9417           return MF_NO_ACTION;
9418
9419         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9420           return MF_NO_ACTION;
9421
9422         if (player->push_delay == 0)    /* new pushing; restart delay */
9423           player->push_delay = FrameCounter;
9424
9425         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9426             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9427             element != EL_SPRING && element != EL_BALLOON)
9428         {
9429           /* make sure that there is no move delay before next try to push */
9430           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9431             player->move_delay = INITIAL_MOVE_DELAY_OFF;
9432
9433           return MF_NO_ACTION;
9434         }
9435
9436 #if 0
9437         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9438 #endif
9439
9440         if (IS_SB_ELEMENT(element))
9441         {
9442           if (element == EL_SOKOBAN_FIELD_FULL)
9443           {
9444             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9445             local_player->sokobanfields_still_needed++;
9446           }
9447
9448           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9449           {
9450             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9451             local_player->sokobanfields_still_needed--;
9452           }
9453
9454           Feld[x][y] = EL_SOKOBAN_OBJECT;
9455
9456           if (Back[x][y] == Back[nextx][nexty])
9457             PlayLevelSoundAction(x, y, ACTION_PUSHING);
9458           else if (Back[x][y] != 0)
9459             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9460                                         ACTION_EMPTYING);
9461           else
9462             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9463                                         ACTION_FILLING);
9464
9465           if (local_player->sokobanfields_still_needed == 0 &&
9466               game.emulation == EMU_SOKOBAN)
9467           {
9468             player->LevelSolved = player->GameOver = TRUE;
9469             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9470           }
9471         }
9472         else
9473           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9474
9475         InitMovingField(x, y, move_direction);
9476         GfxAction[x][y] = ACTION_PUSHING;
9477
9478         if (mode == DF_SNAP)
9479           ContinueMoving(x, y);
9480         else
9481           MovPos[x][y] = (dx != 0 ? dx : dy);
9482
9483         Pushed[x][y] = TRUE;
9484         Pushed[nextx][nexty] = TRUE;
9485
9486         if (game.engine_version < VERSION_IDENT(2,2,0,7))
9487           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9488         else
9489           player->push_delay_value = -1;        /* get new value later */
9490
9491         CheckTriggeredElementSideChange(x, y, element, dig_side,
9492                                         CE_OTHER_GETS_PUSHED);
9493         CheckElementSideChange(x, y, element, dig_side,
9494                                CE_PUSHED_BY_PLAYER, -1);
9495
9496         break;
9497       }
9498       else if (IS_SWITCHABLE(element))
9499       {
9500         if (PLAYER_SWITCHING(player, x, y))
9501           return MF_ACTION;
9502
9503         player->is_switching = TRUE;
9504         player->switch_x = x;
9505         player->switch_y = y;
9506
9507         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9508
9509         if (element == EL_ROBOT_WHEEL)
9510         {
9511           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9512           ZX = x;
9513           ZY = y;
9514
9515           DrawLevelField(x, y);
9516         }
9517         else if (element == EL_SP_TERMINAL)
9518         {
9519           int xx, yy;
9520
9521           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9522           {
9523             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9524               Bang(xx, yy);
9525             else if (Feld[xx][yy] == EL_SP_TERMINAL)
9526               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9527           }
9528         }
9529         else if (IS_BELT_SWITCH(element))
9530         {
9531           ToggleBeltSwitch(x, y);
9532         }
9533         else if (element == EL_SWITCHGATE_SWITCH_UP ||
9534                  element == EL_SWITCHGATE_SWITCH_DOWN)
9535         {
9536           ToggleSwitchgateSwitch(x, y);
9537         }
9538         else if (element == EL_LIGHT_SWITCH ||
9539                  element == EL_LIGHT_SWITCH_ACTIVE)
9540         {
9541           ToggleLightSwitch(x, y);
9542
9543 #if 0
9544           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9545                          SND_LIGHT_SWITCH_ACTIVATING :
9546                          SND_LIGHT_SWITCH_DEACTIVATING);
9547 #endif
9548         }
9549         else if (element == EL_TIMEGATE_SWITCH)
9550         {
9551           ActivateTimegateSwitch(x, y);
9552         }
9553         else if (element == EL_BALLOON_SWITCH_LEFT ||
9554                  element == EL_BALLOON_SWITCH_RIGHT ||
9555                  element == EL_BALLOON_SWITCH_UP ||
9556                  element == EL_BALLOON_SWITCH_DOWN ||
9557                  element == EL_BALLOON_SWITCH_ANY)
9558         {
9559           if (element == EL_BALLOON_SWITCH_ANY)
9560             game.balloon_dir = move_direction;
9561           else
9562             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9563                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9564                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9565                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9566                                 MV_NO_MOVING);
9567         }
9568         else if (element == EL_LAMP)
9569         {
9570           Feld[x][y] = EL_LAMP_ACTIVE;
9571           local_player->lights_still_needed--;
9572
9573           DrawLevelField(x, y);
9574         }
9575         else if (element == EL_TIME_ORB_FULL)
9576         {
9577           Feld[x][y] = EL_TIME_ORB_EMPTY;
9578           TimeLeft += 10;
9579           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9580
9581           DrawLevelField(x, y);
9582
9583 #if 0
9584           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9585 #endif
9586         }
9587
9588         return MF_ACTION;
9589       }
9590       else
9591       {
9592         if (!PLAYER_SWITCHING(player, x, y))
9593         {
9594           player->is_switching = TRUE;
9595           player->switch_x = x;
9596           player->switch_y = y;
9597
9598           CheckTriggeredElementSideChange(x, y, element, dig_side,
9599                                           CE_OTHER_IS_SWITCHING);
9600           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9601         }
9602
9603         CheckTriggeredElementSideChange(x, y, element, dig_side,
9604                                         CE_OTHER_GETS_PRESSED);
9605         CheckElementSideChange(x, y, element, dig_side,
9606                                CE_PRESSED_BY_PLAYER, -1);
9607       }
9608
9609       return MF_NO_ACTION;
9610   }
9611
9612   player->push_delay = 0;
9613
9614   if (Feld[x][y] != element)            /* really digged/collected something */
9615     player->is_collecting = !player->is_digging;
9616
9617   return MF_MOVING;
9618 }
9619
9620 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9621 {
9622   int jx = player->jx, jy = player->jy;
9623   int x = jx + dx, y = jy + dy;
9624   int snap_direction = (dx == -1 ? MV_LEFT :
9625                         dx == +1 ? MV_RIGHT :
9626                         dy == -1 ? MV_UP :
9627                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9628
9629   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9630     return FALSE;
9631
9632   if (!player->active || !IN_LEV_FIELD(x, y))
9633     return FALSE;
9634
9635   if (dx && dy)
9636     return FALSE;
9637
9638   if (!dx && !dy)
9639   {
9640     if (player->MovPos == 0)
9641       player->is_pushing = FALSE;
9642
9643     player->is_snapping = FALSE;
9644
9645     if (player->MovPos == 0)
9646     {
9647       player->is_moving = FALSE;
9648       player->is_digging = FALSE;
9649       player->is_collecting = FALSE;
9650     }
9651
9652     return FALSE;
9653   }
9654
9655   if (player->is_snapping)
9656     return FALSE;
9657
9658   player->MovDir = snap_direction;
9659
9660 #if 1
9661   if (player->MovPos == 0)
9662 #endif
9663   {
9664     player->is_moving = FALSE;
9665     player->is_digging = FALSE;
9666     player->is_collecting = FALSE;
9667   }
9668
9669   player->is_dropping = FALSE;
9670
9671   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9672     return FALSE;
9673
9674   player->is_snapping = TRUE;
9675
9676 #if 1
9677   if (player->MovPos == 0)
9678 #endif
9679   {
9680     player->is_moving = FALSE;
9681     player->is_digging = FALSE;
9682     player->is_collecting = FALSE;
9683   }
9684
9685   DrawLevelField(x, y);
9686   BackToFront();
9687
9688   return TRUE;
9689 }
9690
9691 boolean DropElement(struct PlayerInfo *player)
9692 {
9693   int jx = player->jx, jy = player->jy;
9694   int old_element = Feld[jx][jy];
9695   int new_element;
9696
9697   /* check if player is active, not moving and ready to drop */
9698   if (!player->active || player->MovPos || player->drop_delay > 0)
9699     return FALSE;
9700
9701   /* check if player has anything that can be dropped */
9702   if (player->inventory_size == 0 && player->dynabombs_left == 0)
9703     return FALSE;
9704
9705   /* check if anything can be dropped at the current position */
9706   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9707     return FALSE;
9708
9709   /* collected custom elements can only be dropped on empty fields */
9710   if (player->inventory_size > 0 &&
9711       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9712       && old_element != EL_EMPTY)
9713     return FALSE;
9714
9715   if (old_element != EL_EMPTY)
9716     Back[jx][jy] = old_element;         /* store old element on this field */
9717
9718   ResetGfxAnimation(jx, jy);
9719   ResetRandomAnimationValue(jx, jy);
9720
9721   if (player->inventory_size > 0)
9722   {
9723     player->inventory_size--;
9724     new_element = player->inventory_element[player->inventory_size];
9725
9726     if (new_element == EL_DYNAMITE)
9727       new_element = EL_DYNAMITE_ACTIVE;
9728     else if (new_element == EL_SP_DISK_RED)
9729       new_element = EL_SP_DISK_RED_ACTIVE;
9730
9731     Feld[jx][jy] = new_element;
9732
9733     DrawText(DX_DYNAMITE, DY_DYNAMITE,
9734              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9735
9736     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9737       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9738
9739     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9740
9741 #if 1
9742     /* needed if previous element just changed to "empty" in the last frame */
9743     Changed[jx][jy] = 0;                /* allow another change */
9744 #endif
9745
9746     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9747     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9748
9749     TestIfElementTouchesCustomElement(jx, jy);
9750   }
9751   else          /* player is dropping a dyna bomb */
9752   {
9753     player->dynabombs_left--;
9754     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9755
9756     Feld[jx][jy] = new_element;
9757
9758     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9759       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9760
9761     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9762   }
9763
9764
9765
9766 #if 1
9767
9768   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
9769   {
9770     InitField(jx, jy, FALSE);
9771     if (CAN_MOVE(Feld[jx][jy]))
9772       InitMovDir(jx, jy);
9773   }
9774
9775   new_element = Feld[jx][jy];
9776
9777   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9778       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9779   {
9780     int move_stepsize = element_info[new_element].move_stepsize;
9781     int direction, dx, dy, nextx, nexty;
9782
9783     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9784       MovDir[jx][jy] = player->MovDir;
9785
9786     direction = MovDir[jx][jy];
9787     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9788     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9789     nextx = jx + dx;
9790     nexty = jy + dy;
9791
9792     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9793     {
9794 #if 0
9795       WasJustMoving[jx][jy] = 3;
9796 #else
9797       InitMovingField(jx, jy, direction);
9798       ContinueMoving(jx, jy);
9799 #endif
9800     }
9801     else
9802     {
9803       Changed[jx][jy] = 0;              /* allow another change */
9804
9805 #if 1
9806       TestIfElementHitsCustomElement(jx, jy, direction);
9807 #else
9808       CheckElementSideChange(jx, jy, new_element,
9809                              direction, CE_HITTING_SOMETHING, -1);
9810 #endif
9811     }
9812
9813     player->drop_delay = 2 * TILEX / move_stepsize + 1;
9814   }
9815
9816 #if 0
9817   player->drop_delay = 8 + 8 + 8;
9818 #endif
9819
9820 #endif
9821
9822   player->is_dropping = TRUE;
9823
9824
9825   return TRUE;
9826 }
9827
9828 /* ------------------------------------------------------------------------- */
9829 /* game sound playing functions                                              */
9830 /* ------------------------------------------------------------------------- */
9831
9832 static int *loop_sound_frame = NULL;
9833 static int *loop_sound_volume = NULL;
9834
9835 void InitPlayLevelSound()
9836 {
9837   int num_sounds = getSoundListSize();
9838
9839   checked_free(loop_sound_frame);
9840   checked_free(loop_sound_volume);
9841
9842   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
9843   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9844 }
9845
9846 static void PlayLevelSound(int x, int y, int nr)
9847 {
9848   int sx = SCREENX(x), sy = SCREENY(y);
9849   int volume, stereo_position;
9850   int max_distance = 8;
9851   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9852
9853   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9854       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9855     return;
9856
9857   if (!IN_LEV_FIELD(x, y) ||
9858       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9859       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9860     return;
9861
9862   volume = SOUND_MAX_VOLUME;
9863
9864   if (!IN_SCR_FIELD(sx, sy))
9865   {
9866     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9867     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9868
9869     volume -= volume * (dx > dy ? dx : dy) / max_distance;
9870   }
9871
9872   stereo_position = (SOUND_MAX_LEFT +
9873                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9874                      (SCR_FIELDX + 2 * max_distance));
9875
9876   if (IS_LOOP_SOUND(nr))
9877   {
9878     /* This assures that quieter loop sounds do not overwrite louder ones,
9879        while restarting sound volume comparison with each new game frame. */
9880
9881     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9882       return;
9883
9884     loop_sound_volume[nr] = volume;
9885     loop_sound_frame[nr] = FrameCounter;
9886   }
9887
9888   PlaySoundExt(nr, volume, stereo_position, type);
9889 }
9890
9891 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9892 {
9893   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9894                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
9895                  y < LEVELY(BY1) ? LEVELY(BY1) :
9896                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
9897                  sound_action);
9898 }
9899
9900 static void PlayLevelSoundAction(int x, int y, int action)
9901 {
9902   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9903 }
9904
9905 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9906 {
9907   int sound_effect = element_info[element].sound[action];
9908
9909   if (sound_effect != SND_UNDEFINED)
9910     PlayLevelSound(x, y, sound_effect);
9911 }
9912
9913 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9914                                               int action)
9915 {
9916   int sound_effect = element_info[element].sound[action];
9917
9918   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9919     PlayLevelSound(x, y, sound_effect);
9920 }
9921
9922 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9923 {
9924   int sound_effect = element_info[Feld[x][y]].sound[action];
9925
9926   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9927     PlayLevelSound(x, y, sound_effect);
9928 }
9929
9930 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9931 {
9932   int sound_effect = element_info[Feld[x][y]].sound[action];
9933
9934   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9935     StopSound(sound_effect);
9936 }
9937
9938 static void PlayLevelMusic()
9939 {
9940   if (levelset.music[level_nr] != MUS_UNDEFINED)
9941     PlayMusic(levelset.music[level_nr]);        /* from config file */
9942   else
9943     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
9944 }
9945
9946 void RaiseScore(int value)
9947 {
9948   local_player->score += value;
9949   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9950 }
9951
9952 void RaiseScoreElement(int element)
9953 {
9954   switch(element)
9955   {
9956     case EL_EMERALD:
9957     case EL_BD_DIAMOND:
9958     case EL_EMERALD_YELLOW:
9959     case EL_EMERALD_RED:
9960     case EL_EMERALD_PURPLE:
9961     case EL_SP_INFOTRON:
9962       RaiseScore(level.score[SC_EMERALD]);
9963       break;
9964     case EL_DIAMOND:
9965       RaiseScore(level.score[SC_DIAMOND]);
9966       break;
9967     case EL_CRYSTAL:
9968       RaiseScore(level.score[SC_CRYSTAL]);
9969       break;
9970     case EL_PEARL:
9971       RaiseScore(level.score[SC_PEARL]);
9972       break;
9973     case EL_BUG:
9974     case EL_BD_BUTTERFLY:
9975     case EL_SP_ELECTRON:
9976       RaiseScore(level.score[SC_BUG]);
9977       break;
9978     case EL_SPACESHIP:
9979     case EL_BD_FIREFLY:
9980     case EL_SP_SNIKSNAK:
9981       RaiseScore(level.score[SC_SPACESHIP]);
9982       break;
9983     case EL_YAMYAM:
9984     case EL_DARK_YAMYAM:
9985       RaiseScore(level.score[SC_YAMYAM]);
9986       break;
9987     case EL_ROBOT:
9988       RaiseScore(level.score[SC_ROBOT]);
9989       break;
9990     case EL_PACMAN:
9991       RaiseScore(level.score[SC_PACMAN]);
9992       break;
9993     case EL_NUT:
9994       RaiseScore(level.score[SC_NUT]);
9995       break;
9996     case EL_DYNAMITE:
9997     case EL_SP_DISK_RED:
9998     case EL_DYNABOMB_INCREASE_NUMBER:
9999     case EL_DYNABOMB_INCREASE_SIZE:
10000     case EL_DYNABOMB_INCREASE_POWER:
10001       RaiseScore(level.score[SC_DYNAMITE]);
10002       break;
10003     case EL_SHIELD_NORMAL:
10004     case EL_SHIELD_DEADLY:
10005       RaiseScore(level.score[SC_SHIELD]);
10006       break;
10007     case EL_EXTRA_TIME:
10008       RaiseScore(level.score[SC_TIME_BONUS]);
10009       break;
10010     case EL_KEY_1:
10011     case EL_KEY_2:
10012     case EL_KEY_3:
10013     case EL_KEY_4:
10014       RaiseScore(level.score[SC_KEY]);
10015       break;
10016     default:
10017       RaiseScore(element_info[element].collect_score);
10018       break;
10019   }
10020 }
10021
10022 void RequestQuitGame(boolean ask_if_really_quit)
10023 {
10024   if (AllPlayersGone ||
10025       !ask_if_really_quit ||
10026       level_editor_test_game ||
10027       Request("Do you really want to quit the game ?",
10028               REQ_ASK | REQ_STAY_CLOSED))
10029   {
10030 #if defined(PLATFORM_UNIX)
10031     if (options.network)
10032       SendToServer_StopPlaying();
10033     else
10034 #endif
10035     {
10036       game_status = GAME_MODE_MAIN;
10037       DrawMainMenu();
10038     }
10039   }
10040   else
10041   {
10042     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10043   }
10044 }
10045
10046
10047 /* ---------- new game button stuff ---------------------------------------- */
10048
10049 /* graphic position values for game buttons */
10050 #define GAME_BUTTON_XSIZE       30
10051 #define GAME_BUTTON_YSIZE       30
10052 #define GAME_BUTTON_XPOS        5
10053 #define GAME_BUTTON_YPOS        215
10054 #define SOUND_BUTTON_XPOS       5
10055 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10056
10057 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10058 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10059 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10060 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10061 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10062 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10063
10064 static struct
10065 {
10066   int x, y;
10067   int gadget_id;
10068   char *infotext;
10069 } gamebutton_info[NUM_GAME_BUTTONS] =
10070 {
10071   {
10072     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10073     GAME_CTRL_ID_STOP,
10074     "stop game"
10075   },
10076   {
10077     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10078     GAME_CTRL_ID_PAUSE,
10079     "pause game"
10080   },
10081   {
10082     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10083     GAME_CTRL_ID_PLAY,
10084     "play game"
10085   },
10086   {
10087     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10088     SOUND_CTRL_ID_MUSIC,
10089     "background music on/off"
10090   },
10091   {
10092     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10093     SOUND_CTRL_ID_LOOPS,
10094     "sound loops on/off"
10095   },
10096   {
10097     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10098     SOUND_CTRL_ID_SIMPLE,
10099     "normal sounds on/off"
10100   }
10101 };
10102
10103 void CreateGameButtons()
10104 {
10105   int i;
10106
10107   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10108   {
10109     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10110     struct GadgetInfo *gi;
10111     int button_type;
10112     boolean checked;
10113     unsigned long event_mask;
10114     int gd_xoffset, gd_yoffset;
10115     int gd_x1, gd_x2, gd_y1, gd_y2;
10116     int id = i;
10117
10118     gd_xoffset = gamebutton_info[i].x;
10119     gd_yoffset = gamebutton_info[i].y;
10120     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10121     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10122
10123     if (id == GAME_CTRL_ID_STOP ||
10124         id == GAME_CTRL_ID_PAUSE ||
10125         id == GAME_CTRL_ID_PLAY)
10126     {
10127       button_type = GD_TYPE_NORMAL_BUTTON;
10128       checked = FALSE;
10129       event_mask = GD_EVENT_RELEASED;
10130       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10131       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10132     }
10133     else
10134     {
10135       button_type = GD_TYPE_CHECK_BUTTON;
10136       checked =
10137         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10138          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10139          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10140       event_mask = GD_EVENT_PRESSED;
10141       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10142       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10143     }
10144
10145     gi = CreateGadget(GDI_CUSTOM_ID, id,
10146                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10147                       GDI_X, DX + gd_xoffset,
10148                       GDI_Y, DY + gd_yoffset,
10149                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10150                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10151                       GDI_TYPE, button_type,
10152                       GDI_STATE, GD_BUTTON_UNPRESSED,
10153                       GDI_CHECKED, checked,
10154                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10155                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10156                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10157                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10158                       GDI_EVENT_MASK, event_mask,
10159                       GDI_CALLBACK_ACTION, HandleGameButtons,
10160                       GDI_END);
10161
10162     if (gi == NULL)
10163       Error(ERR_EXIT, "cannot create gadget");
10164
10165     game_gadget[id] = gi;
10166   }
10167 }
10168
10169 void FreeGameButtons()
10170 {
10171   int i;
10172
10173   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10174     FreeGadget(game_gadget[i]);
10175 }
10176
10177 static void MapGameButtons()
10178 {
10179   int i;
10180
10181   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10182     MapGadget(game_gadget[i]);
10183 }
10184
10185 void UnmapGameButtons()
10186 {
10187   int i;
10188
10189   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10190     UnmapGadget(game_gadget[i]);
10191 }
10192
10193 static void HandleGameButtons(struct GadgetInfo *gi)
10194 {
10195   int id = gi->custom_id;
10196
10197   if (game_status != GAME_MODE_PLAYING)
10198     return;
10199
10200   switch (id)
10201   {
10202     case GAME_CTRL_ID_STOP:
10203       RequestQuitGame(TRUE);
10204       break;
10205
10206     case GAME_CTRL_ID_PAUSE:
10207       if (options.network)
10208       {
10209 #if defined(PLATFORM_UNIX)
10210         if (tape.pausing)
10211           SendToServer_ContinuePlaying();
10212         else
10213           SendToServer_PausePlaying();
10214 #endif
10215       }
10216       else
10217         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10218       break;
10219
10220     case GAME_CTRL_ID_PLAY:
10221       if (tape.pausing)
10222       {
10223 #if defined(PLATFORM_UNIX)
10224         if (options.network)
10225           SendToServer_ContinuePlaying();
10226         else
10227 #endif
10228         {
10229           tape.pausing = FALSE;
10230           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10231         }
10232       }
10233       break;
10234
10235     case SOUND_CTRL_ID_MUSIC:
10236       if (setup.sound_music)
10237       { 
10238         setup.sound_music = FALSE;
10239         FadeMusic();
10240       }
10241       else if (audio.music_available)
10242       { 
10243         setup.sound = setup.sound_music = TRUE;
10244
10245         SetAudioMode(setup.sound);
10246
10247         PlayLevelMusic();
10248       }
10249       break;
10250
10251     case SOUND_CTRL_ID_LOOPS:
10252       if (setup.sound_loops)
10253         setup.sound_loops = FALSE;
10254       else if (audio.loops_available)
10255       {
10256         setup.sound = setup.sound_loops = TRUE;
10257         SetAudioMode(setup.sound);
10258       }
10259       break;
10260
10261     case SOUND_CTRL_ID_SIMPLE:
10262       if (setup.sound_simple)
10263         setup.sound_simple = FALSE;
10264       else if (audio.sound_available)
10265       {
10266         setup.sound = setup.sound_simple = TRUE;
10267         SetAudioMode(setup.sound);
10268       }
10269       break;
10270
10271     default:
10272       break;
10273   }
10274 }