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