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