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