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