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