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