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