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