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