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