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