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