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