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