rnd-20040110-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 (player_action)
6488   {
6489 #if 0
6490     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6491 #endif
6492
6493     if (button1)
6494       snapped = SnapField(player, dx, dy);
6495     else
6496     {
6497       if (button2)
6498         dropped = DropElement(player);
6499
6500       moved = MovePlayer(player, dx, dy);
6501     }
6502
6503     if (tape.single_step && tape.recording && !tape.pausing)
6504     {
6505       if (button1 || (dropped && !moved))
6506       {
6507         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6508         SnapField(player, 0, 0);                /* stop snapping */
6509       }
6510     }
6511
6512     SetPlayerWaiting(player, FALSE);
6513
6514 #if 1
6515     return player_action;
6516 #else
6517     stored_player_action[player->index_nr] = player_action;
6518 #endif
6519   }
6520   else
6521   {
6522 #if 0
6523     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6524 #endif
6525
6526     /* no actions for this player (no input at player's configured device) */
6527
6528     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6529     SnapField(player, 0, 0);
6530     CheckGravityMovement(player);
6531
6532     if (player->MovPos == 0)
6533       SetPlayerWaiting(player, TRUE);
6534
6535     if (player->MovPos == 0)    /* needed for tape.playing */
6536       player->is_moving = FALSE;
6537
6538     player->is_dropping = FALSE;
6539
6540     return 0;
6541   }
6542
6543 #if 0
6544   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6545   {
6546     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6547
6548     TapeRecordAction(stored_player_action);
6549     num_stored_actions = 0;
6550   }
6551 #endif
6552 }
6553
6554 #else
6555
6556 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6557 {
6558   static byte stored_player_action[MAX_PLAYERS];
6559   static int num_stored_actions = 0;
6560   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6561   int left      = player_action & JOY_LEFT;
6562   int right     = player_action & JOY_RIGHT;
6563   int up        = player_action & JOY_UP;
6564   int down      = player_action & JOY_DOWN;
6565   int button1   = player_action & JOY_BUTTON_1;
6566   int button2   = player_action & JOY_BUTTON_2;
6567   int dx        = (left ? -1    : right ? 1     : 0);
6568   int dy        = (up   ? -1    : down  ? 1     : 0);
6569
6570   stored_player_action[player->index_nr] = 0;
6571   num_stored_actions++;
6572
6573   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6574
6575   if (!player->active || tape.pausing)
6576     return;
6577
6578   if (player_action)
6579   {
6580     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6581
6582     if (button1)
6583       snapped = SnapField(player, dx, dy);
6584     else
6585     {
6586       if (button2)
6587         dropped = DropElement(player);
6588
6589       moved = MovePlayer(player, dx, dy);
6590     }
6591
6592     if (tape.single_step && tape.recording && !tape.pausing)
6593     {
6594       if (button1 || (dropped && !moved))
6595       {
6596         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6597         SnapField(player, 0, 0);                /* stop snapping */
6598       }
6599     }
6600
6601     stored_player_action[player->index_nr] = player_action;
6602   }
6603   else
6604   {
6605     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6606
6607     /* no actions for this player (no input at player's configured device) */
6608
6609     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6610     SnapField(player, 0, 0);
6611     CheckGravityMovement(player);
6612
6613     if (player->MovPos == 0)
6614       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6615
6616     if (player->MovPos == 0)    /* needed for tape.playing */
6617       player->is_moving = FALSE;
6618   }
6619
6620   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6621   {
6622     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6623
6624     TapeRecordAction(stored_player_action);
6625     num_stored_actions = 0;
6626   }
6627 }
6628 #endif
6629
6630 void GameActions()
6631 {
6632   static unsigned long action_delay = 0;
6633   unsigned long action_delay_value;
6634   int magic_wall_x = 0, magic_wall_y = 0;
6635   int i, x, y, element, graphic;
6636   byte *recorded_player_action;
6637   byte summarized_player_action = 0;
6638 #if 1
6639   byte tape_action[MAX_PLAYERS];
6640 #endif
6641
6642   if (game_status != GAME_MODE_PLAYING)
6643     return;
6644
6645   action_delay_value =
6646     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6647
6648   if (tape.playing && tape.index_search && !tape.pausing)
6649     action_delay_value = 0;
6650
6651   /* ---------- main game synchronization point ---------- */
6652
6653   WaitUntilDelayReached(&action_delay, action_delay_value);
6654
6655   if (network_playing && !network_player_action_received)
6656   {
6657     /*
6658 #ifdef DEBUG
6659     printf("DEBUG: try to get network player actions in time\n");
6660 #endif
6661     */
6662
6663 #if defined(PLATFORM_UNIX)
6664     /* last chance to get network player actions without main loop delay */
6665     HandleNetworking();
6666 #endif
6667
6668     if (game_status != GAME_MODE_PLAYING)
6669       return;
6670
6671     if (!network_player_action_received)
6672     {
6673       /*
6674 #ifdef DEBUG
6675       printf("DEBUG: failed to get network player actions in time\n");
6676 #endif
6677       */
6678       return;
6679     }
6680   }
6681
6682   if (tape.pausing)
6683     return;
6684
6685 #if 0
6686   printf("::: getting new tape action [%d]\n", FrameCounter);
6687 #endif
6688
6689   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6690
6691   for (i = 0; i < MAX_PLAYERS; i++)
6692   {
6693     summarized_player_action |= stored_player[i].action;
6694
6695     if (!network_playing)
6696       stored_player[i].effective_action = stored_player[i].action;
6697   }
6698
6699 #if defined(PLATFORM_UNIX)
6700   if (network_playing)
6701     SendToServer_MovePlayer(summarized_player_action);
6702 #endif
6703
6704   if (!options.network && !setup.team_mode)
6705     local_player->effective_action = summarized_player_action;
6706
6707   for (i = 0; i < MAX_PLAYERS; i++)
6708   {
6709     int actual_player_action = stored_player[i].effective_action;
6710
6711     if (stored_player[i].programmed_action)
6712       actual_player_action = stored_player[i].programmed_action;
6713
6714     if (recorded_player_action)
6715       actual_player_action = recorded_player_action[i];
6716
6717     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6718
6719     if (tape.recording && tape_action[i] && !tape.player_participates[i])
6720       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
6721
6722     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6723   }
6724
6725 #if 1
6726   if (tape.recording)
6727     TapeRecordAction(tape_action);
6728 #endif
6729
6730   network_player_action_received = FALSE;
6731
6732   ScrollScreen(NULL, SCROLL_GO_ON);
6733
6734 #if 0
6735   FrameCounter++;
6736   TimeFrames++;
6737
6738   for (i = 0; i < MAX_PLAYERS; i++)
6739     stored_player[i].Frame++;
6740 #endif
6741
6742 #if 1
6743   if (game.engine_version < VERSION_IDENT(2,2,0,7))
6744   {
6745     for (i = 0; i < MAX_PLAYERS; i++)
6746     {
6747       struct PlayerInfo *player = &stored_player[i];
6748       int x = player->jx;
6749       int y = player->jy;
6750
6751       if (player->active && player->is_pushing && player->is_moving &&
6752           IS_MOVING(x, y))
6753       {
6754         ContinueMoving(x, y);
6755
6756         /* continue moving after pushing (this is actually a bug) */
6757         if (!IS_MOVING(x, y))
6758         {
6759           Stop[x][y] = FALSE;
6760         }
6761       }
6762     }
6763   }
6764 #endif
6765
6766   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6767   {
6768     Changed[x][y] = CE_BITMASK_DEFAULT;
6769     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6770
6771 #if DEBUG
6772     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6773     {
6774       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6775       printf("GameActions(): This should never happen!\n");
6776
6777       ChangePage[x][y] = -1;
6778     }
6779 #endif
6780
6781     Stop[x][y] = FALSE;
6782     if (WasJustMoving[x][y] > 0)
6783       WasJustMoving[x][y]--;
6784     if (WasJustFalling[x][y] > 0)
6785       WasJustFalling[x][y]--;
6786
6787     GfxFrame[x][y]++;
6788
6789 #if 1
6790     /* reset finished pushing action (not done in ContinueMoving() to allow
6791        continous pushing animation for elements with zero push delay) */
6792     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6793     {
6794       ResetGfxAnimation(x, y);
6795       DrawLevelField(x, y);
6796     }
6797 #endif
6798
6799 #if DEBUG
6800     if (IS_BLOCKED(x, y))
6801     {
6802       int oldx, oldy;
6803
6804       Blocked2Moving(x, y, &oldx, &oldy);
6805       if (!IS_MOVING(oldx, oldy))
6806       {
6807         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6808         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6809         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6810         printf("GameActions(): This should never happen!\n");
6811       }
6812     }
6813 #endif
6814   }
6815
6816   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6817   {
6818     element = Feld[x][y];
6819 #if 1
6820     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6821 #else
6822     graphic = el2img(element);
6823 #endif
6824
6825 #if 0
6826     if (element == -1)
6827     {
6828       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6829
6830       element = graphic = 0;
6831     }
6832 #endif
6833
6834     if (graphic_info[graphic].anim_global_sync)
6835       GfxFrame[x][y] = FrameCounter;
6836
6837     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6838         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6839       ResetRandomAnimationValue(x, y);
6840
6841     SetRandomAnimationValue(x, y);
6842
6843 #if 1
6844     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6845 #endif
6846
6847     if (IS_INACTIVE(element))
6848     {
6849       if (IS_ANIMATED(graphic))
6850         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6851
6852       continue;
6853     }
6854
6855 #if 1
6856     /* this may take place after moving, so 'element' may have changed */
6857 #if 0
6858     if (IS_CHANGING(x, y))
6859 #else
6860     if (IS_CHANGING(x, y) &&
6861         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6862 #endif
6863     {
6864 #if 0
6865       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6866                     element_info[element].event_page_nr[CE_DELAY]);
6867 #else
6868       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6869 #endif
6870
6871       element = Feld[x][y];
6872       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6873     }
6874 #endif
6875
6876     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6877     {
6878       StartMoving(x, y);
6879
6880 #if 1
6881       element = Feld[x][y];
6882       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6883 #if 0
6884       if (element == EL_MOLE)
6885         printf("::: %d, %d, %d [%d]\n",
6886                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6887                GfxAction[x][y]);
6888 #endif
6889 #if 0
6890       if (element == EL_YAMYAM)
6891         printf("::: %d, %d, %d\n",
6892                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6893 #endif
6894 #endif
6895
6896       if (IS_ANIMATED(graphic) &&
6897           !IS_MOVING(x, y) &&
6898           !Stop[x][y])
6899       {
6900         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6901
6902 #if 0
6903         if (element == EL_BUG)
6904           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6905 #endif
6906
6907 #if 0
6908         if (element == EL_MOLE)
6909           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6910 #endif
6911       }
6912
6913       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6914         EdelsteinFunkeln(x, y);
6915     }
6916     else if ((element == EL_ACID ||
6917               element == EL_EXIT_OPEN ||
6918               element == EL_SP_EXIT_OPEN ||
6919               element == EL_SP_TERMINAL ||
6920               element == EL_SP_TERMINAL_ACTIVE ||
6921               element == EL_EXTRA_TIME ||
6922               element == EL_SHIELD_NORMAL ||
6923               element == EL_SHIELD_DEADLY) &&
6924              IS_ANIMATED(graphic))
6925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6926     else if (IS_MOVING(x, y))
6927       ContinueMoving(x, y);
6928     else if (IS_ACTIVE_BOMB(element))
6929       CheckDynamite(x, y);
6930 #if 0
6931     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6932       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6933 #endif
6934     else if (element == EL_AMOEBA_GROWING)
6935       AmoebeWaechst(x, y);
6936     else if (element == EL_AMOEBA_SHRINKING)
6937       AmoebaDisappearing(x, y);
6938
6939 #if !USE_NEW_AMOEBA_CODE
6940     else if (IS_AMOEBALIVE(element))
6941       AmoebeAbleger(x, y);
6942 #endif
6943
6944     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6945       Life(x, y);
6946     else if (element == EL_EXIT_CLOSED)
6947       CheckExit(x, y);
6948     else if (element == EL_SP_EXIT_CLOSED)
6949       CheckExitSP(x, y);
6950     else if (element == EL_EXPANDABLE_WALL_GROWING)
6951       MauerWaechst(x, y);
6952     else if (element == EL_EXPANDABLE_WALL ||
6953              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6954              element == EL_EXPANDABLE_WALL_VERTICAL ||
6955              element == EL_EXPANDABLE_WALL_ANY)
6956       MauerAbleger(x, y);
6957     else if (element == EL_FLAMES)
6958       CheckForDragon(x, y);
6959 #if 0
6960     else if (IS_AUTO_CHANGING(element))
6961       ChangeElement(x, y);
6962 #endif
6963     else if (element == EL_EXPLOSION)
6964       ; /* drawing of correct explosion animation is handled separately */
6965     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6966       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6967
6968 #if 0
6969     /* this may take place after moving, so 'element' may have changed */
6970     if (IS_AUTO_CHANGING(Feld[x][y]))
6971       ChangeElement(x, y);
6972 #endif
6973
6974     if (IS_BELT_ACTIVE(element))
6975       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6976
6977     if (game.magic_wall_active)
6978     {
6979       int jx = local_player->jx, jy = local_player->jy;
6980
6981       /* play the element sound at the position nearest to the player */
6982       if ((element == EL_MAGIC_WALL_FULL ||
6983            element == EL_MAGIC_WALL_ACTIVE ||
6984            element == EL_MAGIC_WALL_EMPTYING ||
6985            element == EL_BD_MAGIC_WALL_FULL ||
6986            element == EL_BD_MAGIC_WALL_ACTIVE ||
6987            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6988           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6989       {
6990         magic_wall_x = x;
6991         magic_wall_y = y;
6992       }
6993     }
6994   }
6995
6996 #if USE_NEW_AMOEBA_CODE
6997   /* new experimental amoeba growth stuff */
6998 #if 1
6999   if (!(FrameCounter % 8))
7000 #endif
7001   {
7002     static unsigned long random = 1684108901;
7003
7004     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7005     {
7006 #if 0
7007       x = (random >> 10) % lev_fieldx;
7008       y = (random >> 20) % lev_fieldy;
7009 #else
7010       x = RND(lev_fieldx);
7011       y = RND(lev_fieldy);
7012 #endif
7013       element = Feld[x][y];
7014
7015       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7016       if (!IS_PLAYER(x,y) &&
7017           (element == EL_EMPTY ||
7018            element == EL_SAND ||
7019            element == EL_QUICKSAND_EMPTY ||
7020            element == EL_ACID_SPLASH_LEFT ||
7021            element == EL_ACID_SPLASH_RIGHT))
7022       {
7023         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7024             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7025             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7026             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7027           Feld[x][y] = EL_AMOEBA_DROP;
7028       }
7029
7030       random = random * 129 + 1;
7031     }
7032   }
7033 #endif
7034
7035 #if 0
7036   if (game.explosions_delayed)
7037 #endif
7038   {
7039     game.explosions_delayed = FALSE;
7040
7041     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7042     {
7043       element = Feld[x][y];
7044
7045       if (ExplodeField[x][y])
7046         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7047       else if (element == EL_EXPLOSION)
7048         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7049
7050       ExplodeField[x][y] = EX_NO_EXPLOSION;
7051     }
7052
7053     game.explosions_delayed = TRUE;
7054   }
7055
7056   if (game.magic_wall_active)
7057   {
7058     if (!(game.magic_wall_time_left % 4))
7059     {
7060       int element = Feld[magic_wall_x][magic_wall_y];
7061
7062       if (element == EL_BD_MAGIC_WALL_FULL ||
7063           element == EL_BD_MAGIC_WALL_ACTIVE ||
7064           element == EL_BD_MAGIC_WALL_EMPTYING)
7065         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7066       else
7067         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7068     }
7069
7070     if (game.magic_wall_time_left > 0)
7071     {
7072       game.magic_wall_time_left--;
7073       if (!game.magic_wall_time_left)
7074       {
7075         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7076         {
7077           element = Feld[x][y];
7078
7079           if (element == EL_MAGIC_WALL_ACTIVE ||
7080               element == EL_MAGIC_WALL_FULL)
7081           {
7082             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7083             DrawLevelField(x, y);
7084           }
7085           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7086                    element == EL_BD_MAGIC_WALL_FULL)
7087           {
7088             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7089             DrawLevelField(x, y);
7090           }
7091         }
7092
7093         game.magic_wall_active = FALSE;
7094       }
7095     }
7096   }
7097
7098   if (game.light_time_left > 0)
7099   {
7100     game.light_time_left--;
7101
7102     if (game.light_time_left == 0)
7103       RedrawAllLightSwitchesAndInvisibleElements();
7104   }
7105
7106   if (game.timegate_time_left > 0)
7107   {
7108     game.timegate_time_left--;
7109
7110     if (game.timegate_time_left == 0)
7111       CloseAllOpenTimegates();
7112   }
7113
7114   for (i = 0; i < MAX_PLAYERS; i++)
7115   {
7116     struct PlayerInfo *player = &stored_player[i];
7117
7118     if (SHIELD_ON(player))
7119     {
7120       if (player->shield_deadly_time_left)
7121         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7122       else if (player->shield_normal_time_left)
7123         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7124     }
7125   }
7126
7127   if (TimeFrames >= FRAMES_PER_SECOND)
7128   {
7129     TimeFrames = 0;
7130     TimePlayed++;
7131
7132     for (i = 0; i < MAX_PLAYERS; i++)
7133     {
7134       struct PlayerInfo *player = &stored_player[i];
7135
7136       if (SHIELD_ON(player))
7137       {
7138         player->shield_normal_time_left--;
7139
7140         if (player->shield_deadly_time_left > 0)
7141           player->shield_deadly_time_left--;
7142       }
7143     }
7144
7145     if (tape.recording || tape.playing)
7146       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7147
7148     if (TimeLeft > 0)
7149     {
7150       TimeLeft--;
7151
7152       if (TimeLeft <= 10 && setup.time_limit)
7153         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7154
7155       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7156
7157       if (!TimeLeft && setup.time_limit)
7158         for (i = 0; i < MAX_PLAYERS; i++)
7159           KillHero(&stored_player[i]);
7160     }
7161     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7162       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7163   }
7164
7165   DrawAllPlayers();
7166   PlayAllPlayersSound();
7167
7168   if (options.debug)                    /* calculate frames per second */
7169   {
7170     static unsigned long fps_counter = 0;
7171     static int fps_frames = 0;
7172     unsigned long fps_delay_ms = Counter() - fps_counter;
7173
7174     fps_frames++;
7175
7176     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7177     {
7178       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7179
7180       fps_frames = 0;
7181       fps_counter = Counter();
7182     }
7183
7184     redraw_mask |= REDRAW_FPS;
7185   }
7186
7187 #if 0
7188   if (stored_player[0].jx != stored_player[0].last_jx ||
7189       stored_player[0].jy != stored_player[0].last_jy)
7190     printf("::: %d, %d, %d, %d, %d\n",
7191            stored_player[0].MovDir,
7192            stored_player[0].MovPos,
7193            stored_player[0].GfxPos,
7194            stored_player[0].Frame,
7195            stored_player[0].StepFrame);
7196 #endif
7197
7198 #if 1
7199   FrameCounter++;
7200   TimeFrames++;
7201
7202   for (i = 0; i < MAX_PLAYERS; i++)
7203   {
7204     int move_frames =
7205       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
7206
7207     stored_player[i].Frame += move_frames;
7208
7209     if (stored_player[i].MovPos != 0)
7210       stored_player[i].StepFrame += move_frames;
7211
7212     if (stored_player[i].drop_delay > 0)
7213       stored_player[i].drop_delay--;
7214   }
7215 #endif
7216
7217 #if 1
7218   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7219   {
7220     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7221
7222     local_player->show_envelope = 0;
7223   }
7224 #endif
7225 }
7226
7227 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7228 {
7229   int min_x = x, min_y = y, max_x = x, max_y = y;
7230   int i;
7231
7232   for (i = 0; i < MAX_PLAYERS; i++)
7233   {
7234     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7235
7236     if (!stored_player[i].active || &stored_player[i] == player)
7237       continue;
7238
7239     min_x = MIN(min_x, jx);
7240     min_y = MIN(min_y, jy);
7241     max_x = MAX(max_x, jx);
7242     max_y = MAX(max_y, jy);
7243   }
7244
7245   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7246 }
7247
7248 static boolean AllPlayersInVisibleScreen()
7249 {
7250   int i;
7251
7252   for (i = 0; i < MAX_PLAYERS; i++)
7253   {
7254     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7255
7256     if (!stored_player[i].active)
7257       continue;
7258
7259     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7260       return FALSE;
7261   }
7262
7263   return TRUE;
7264 }
7265
7266 void ScrollLevel(int dx, int dy)
7267 {
7268   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7269   int x, y;
7270
7271   BlitBitmap(drawto_field, drawto_field,
7272              FX + TILEX * (dx == -1) - softscroll_offset,
7273              FY + TILEY * (dy == -1) - softscroll_offset,
7274              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7275              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7276              FX + TILEX * (dx == 1) - softscroll_offset,
7277              FY + TILEY * (dy == 1) - softscroll_offset);
7278
7279   if (dx)
7280   {
7281     x = (dx == 1 ? BX1 : BX2);
7282     for (y = BY1; y <= BY2; y++)
7283       DrawScreenField(x, y);
7284   }
7285
7286   if (dy)
7287   {
7288     y = (dy == 1 ? BY1 : BY2);
7289     for (x = BX1; x <= BX2; x++)
7290       DrawScreenField(x, y);
7291   }
7292
7293   redraw_mask |= REDRAW_FIELD;
7294 }
7295
7296 static void CheckGravityMovement(struct PlayerInfo *player)
7297 {
7298   if (game.gravity && !player->programmed_action)
7299   {
7300     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7301     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7302     int move_dir =
7303       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7304        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7305        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7306     int jx = player->jx, jy = player->jy;
7307     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7308     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7309     int new_jx = jx + dx, new_jy = jy + dy;
7310     boolean field_under_player_is_free =
7311       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7312     boolean player_is_moving_to_valid_field =
7313       (IN_LEV_FIELD(new_jx, new_jy) &&
7314        (Feld[new_jx][new_jy] == EL_SP_BASE ||
7315         Feld[new_jx][new_jy] == EL_SAND));
7316     /* !!! extend EL_SAND to anything diggable !!! */
7317
7318     if (field_under_player_is_free &&
7319         !player_is_moving_to_valid_field &&
7320         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7321       player->programmed_action = MV_DOWN;
7322   }
7323 }
7324
7325 /*
7326   MovePlayerOneStep()
7327   -----------------------------------------------------------------------------
7328   dx, dy:               direction (non-diagonal) to try to move the player to
7329   real_dx, real_dy:     direction as read from input device (can be diagonal)
7330 */
7331
7332 boolean MovePlayerOneStep(struct PlayerInfo *player,
7333                           int dx, int dy, int real_dx, int real_dy)
7334 {
7335 #if 0
7336   static int change_sides[4][2] =
7337   {
7338     /* enter side        leave side */
7339     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
7340     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
7341     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
7342     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
7343   };
7344   int move_direction = (dx == -1 ? MV_LEFT :
7345                         dx == +1 ? MV_RIGHT :
7346                         dy == -1 ? MV_UP :
7347                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7348   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7349   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7350 #endif
7351   int jx = player->jx, jy = player->jy;
7352   int new_jx = jx + dx, new_jy = jy + dy;
7353   int element;
7354   int can_move;
7355
7356   if (!player->active || (!dx && !dy))
7357     return MF_NO_ACTION;
7358
7359   player->MovDir = (dx < 0 ? MV_LEFT :
7360                     dx > 0 ? MV_RIGHT :
7361                     dy < 0 ? MV_UP :
7362                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
7363
7364   if (!IN_LEV_FIELD(new_jx, new_jy))
7365     return MF_NO_ACTION;
7366
7367   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7368     return MF_NO_ACTION;
7369
7370 #if 0
7371   element = MovingOrBlocked2Element(new_jx, new_jy);
7372 #else
7373   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7374 #endif
7375
7376   if (DONT_RUN_INTO(element))
7377   {
7378     if (element == EL_ACID && dx == 0 && dy == 1)
7379     {
7380       SplashAcid(jx, jy);
7381       Feld[jx][jy] = EL_PLAYER_1;
7382       InitMovingField(jx, jy, MV_DOWN);
7383       Store[jx][jy] = EL_ACID;
7384       ContinueMoving(jx, jy);
7385       BuryHero(player);
7386     }
7387     else
7388       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7389
7390     return MF_MOVING;
7391   }
7392
7393   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7394   if (can_move != MF_MOVING)
7395     return can_move;
7396
7397   /* check if DigField() has caused relocation of the player */
7398   if (player->jx != jx || player->jy != jy)
7399     return MF_NO_ACTION;
7400
7401   StorePlayer[jx][jy] = 0;
7402   player->last_jx = jx;
7403   player->last_jy = jy;
7404   player->jx = new_jx;
7405   player->jy = new_jy;
7406   StorePlayer[new_jx][new_jy] = player->element_nr;
7407
7408   player->MovPos =
7409     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7410
7411   player->step_counter++;
7412
7413   player->drop_delay = 0;
7414
7415   PlayerVisit[jx][jy] = FrameCounter;
7416
7417   ScrollPlayer(player, SCROLL_INIT);
7418
7419 #if 0
7420   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7421   {
7422     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7423                                     CE_OTHER_GETS_LEFT);
7424     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7425                            CE_LEFT_BY_PLAYER, -1);
7426   }
7427
7428   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7429   {
7430     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7431                                     enter_side, CE_OTHER_GETS_ENTERED);
7432     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7433                            CE_ENTERED_BY_PLAYER, -1);
7434   }
7435 #endif
7436
7437   return MF_MOVING;
7438 }
7439
7440 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7441 {
7442   int jx = player->jx, jy = player->jy;
7443   int old_jx = jx, old_jy = jy;
7444   int moved = MF_NO_ACTION;
7445
7446 #if 1
7447   if (!player->active)
7448     return FALSE;
7449
7450   if (!dx && !dy)
7451   {
7452     if (player->MovPos == 0)
7453     {
7454       player->is_moving = FALSE;
7455       player->is_digging = FALSE;
7456       player->is_collecting = FALSE;
7457       player->is_snapping = FALSE;
7458       player->is_pushing = FALSE;
7459     }
7460
7461     return FALSE;
7462   }
7463 #else
7464   if (!player->active || (!dx && !dy))
7465     return FALSE;
7466 #endif
7467
7468 #if 0
7469   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7470       !tape.playing)
7471     return FALSE;
7472 #else
7473   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7474       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7475     return FALSE;
7476 #endif
7477
7478   /* remove the last programmed player action */
7479   player->programmed_action = 0;
7480
7481   if (player->MovPos)
7482   {
7483     /* should only happen if pre-1.2 tape recordings are played */
7484     /* this is only for backward compatibility */
7485
7486     int original_move_delay_value = player->move_delay_value;
7487
7488 #if DEBUG
7489     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7490            tape.counter);
7491 #endif
7492
7493     /* scroll remaining steps with finest movement resolution */
7494     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7495
7496     while (player->MovPos)
7497     {
7498       ScrollPlayer(player, SCROLL_GO_ON);
7499       ScrollScreen(NULL, SCROLL_GO_ON);
7500       FrameCounter++;
7501       DrawAllPlayers();
7502       BackToFront();
7503     }
7504
7505     player->move_delay_value = original_move_delay_value;
7506   }
7507
7508   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7509   {
7510     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7511       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7512   }
7513   else
7514   {
7515     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7516       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7517   }
7518
7519   jx = player->jx;
7520   jy = player->jy;
7521
7522   if (moved & MF_MOVING && !ScreenMovPos &&
7523       (player == local_player || !options.network))
7524   {
7525     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7526     int offset = (setup.scroll_delay ? 3 : 0);
7527
7528     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7529     {
7530       /* actual player has left the screen -- scroll in that direction */
7531       if (jx != old_jx)         /* player has moved horizontally */
7532         scroll_x += (jx - old_jx);
7533       else                      /* player has moved vertically */
7534         scroll_y += (jy - old_jy);
7535     }
7536     else
7537     {
7538       if (jx != old_jx)         /* player has moved horizontally */
7539       {
7540         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7541             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7542           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7543
7544         /* don't scroll over playfield boundaries */
7545         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7546           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7547
7548         /* don't scroll more than one field at a time */
7549         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7550
7551         /* don't scroll against the player's moving direction */
7552         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7553             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7554           scroll_x = old_scroll_x;
7555       }
7556       else                      /* player has moved vertically */
7557       {
7558         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7559             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7560           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7561
7562         /* don't scroll over playfield boundaries */
7563         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7564           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7565
7566         /* don't scroll more than one field at a time */
7567         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7568
7569         /* don't scroll against the player's moving direction */
7570         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7571             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7572           scroll_y = old_scroll_y;
7573       }
7574     }
7575
7576     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7577     {
7578       if (!options.network && !AllPlayersInVisibleScreen())
7579       {
7580         scroll_x = old_scroll_x;
7581         scroll_y = old_scroll_y;
7582       }
7583       else
7584       {
7585         ScrollScreen(player, SCROLL_INIT);
7586         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7587       }
7588     }
7589   }
7590
7591 #if 0
7592 #if 1
7593   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7594 #else
7595   if (!(moved & MF_MOVING) && !player->is_pushing)
7596     player->Frame = 0;
7597 #endif
7598 #endif
7599
7600   player->StepFrame = 0;
7601
7602   if (moved & MF_MOVING)
7603   {
7604     if (old_jx != jx && old_jy == jy)
7605       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7606     else if (old_jx == jx && old_jy != jy)
7607       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7608
7609     DrawLevelField(jx, jy);     /* for "crumbled sand" */
7610
7611     player->last_move_dir = player->MovDir;
7612     player->is_moving = TRUE;
7613 #if 1
7614     player->is_snapping = FALSE;
7615 #endif
7616
7617 #if 1
7618     player->is_switching = FALSE;
7619 #endif
7620
7621     player->is_dropping = FALSE;
7622
7623
7624 #if 1
7625     {
7626       static int change_sides[4][2] =
7627       {
7628         /* enter side           leave side */
7629         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
7630         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
7631         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
7632         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
7633       };
7634       int move_direction = player->MovDir;
7635       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7636       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7637
7638 #if 1
7639       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7640       {
7641         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7642                                         leave_side, CE_OTHER_GETS_LEFT);
7643         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7644                                leave_side, CE_LEFT_BY_PLAYER, -1);
7645       }
7646
7647       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7648       {
7649         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7650                                         enter_side, CE_OTHER_GETS_ENTERED);
7651         CheckElementSideChange(jx, jy, Feld[jx][jy],
7652                                enter_side, CE_ENTERED_BY_PLAYER, -1);
7653       }
7654 #endif
7655
7656     }
7657 #endif
7658
7659
7660   }
7661   else
7662   {
7663     CheckGravityMovement(player);
7664
7665     /*
7666     player->last_move_dir = MV_NO_MOVING;
7667     */
7668     player->is_moving = FALSE;
7669   }
7670
7671   if (game.engine_version < VERSION_IDENT(3,0,7,0))
7672   {
7673     TestIfHeroTouchesBadThing(jx, jy);
7674     TestIfPlayerTouchesCustomElement(jx, jy);
7675   }
7676
7677   if (!player->active)
7678     RemoveHero(player);
7679
7680   return moved;
7681 }
7682
7683 void ScrollPlayer(struct PlayerInfo *player, int mode)
7684 {
7685   int jx = player->jx, jy = player->jy;
7686   int last_jx = player->last_jx, last_jy = player->last_jy;
7687   int move_stepsize = TILEX / player->move_delay_value;
7688
7689   if (!player->active || !player->MovPos)
7690     return;
7691
7692   if (mode == SCROLL_INIT)
7693   {
7694     player->actual_frame_counter = FrameCounter;
7695     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7696
7697     if (Feld[last_jx][last_jy] == EL_EMPTY)
7698       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7699
7700 #if 0
7701     DrawPlayer(player);
7702 #endif
7703     return;
7704   }
7705   else if (!FrameReached(&player->actual_frame_counter, 1))
7706     return;
7707
7708   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7709   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7710
7711   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7712     Feld[last_jx][last_jy] = EL_EMPTY;
7713
7714   /* before DrawPlayer() to draw correct player graphic for this case */
7715   if (player->MovPos == 0)
7716     CheckGravityMovement(player);
7717
7718 #if 0
7719   DrawPlayer(player);   /* needed here only to cleanup last field */
7720 #endif
7721
7722   if (player->MovPos == 0)      /* player reached destination field */
7723   {
7724 #if 1
7725     if (player->move_delay_reset_counter > 0)
7726     {
7727       player->move_delay_reset_counter--;
7728
7729       if (player->move_delay_reset_counter == 0)
7730       {
7731         /* continue with normal speed after quickly moving through gate */
7732         HALVE_PLAYER_SPEED(player);
7733
7734         /* be able to make the next move without delay */
7735         player->move_delay = 0;
7736       }
7737     }
7738 #else
7739     if (IS_PASSABLE(Feld[last_jx][last_jy]))
7740     {
7741       /* continue with normal speed after quickly moving through gate */
7742       HALVE_PLAYER_SPEED(player);
7743
7744       /* be able to make the next move without delay */
7745       player->move_delay = 0;
7746     }
7747 #endif
7748
7749     player->last_jx = jx;
7750     player->last_jy = jy;
7751
7752     if (Feld[jx][jy] == EL_EXIT_OPEN ||
7753         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7754         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
7755     {
7756       DrawPlayer(player);       /* needed here only to cleanup last field */
7757       RemoveHero(player);
7758
7759       if (local_player->friends_still_needed == 0 ||
7760           IS_SP_ELEMENT(Feld[jx][jy]))
7761         player->LevelSolved = player->GameOver = TRUE;
7762     }
7763
7764     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7765     {
7766       TestIfHeroTouchesBadThing(jx, jy);
7767       TestIfPlayerTouchesCustomElement(jx, jy);
7768 #if 1
7769       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
7770 #endif
7771
7772       if (!player->active)
7773         RemoveHero(player);
7774     }
7775
7776     if (tape.single_step && tape.recording && !tape.pausing &&
7777         !player->programmed_action)
7778       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7779   }
7780 }
7781
7782 void ScrollScreen(struct PlayerInfo *player, int mode)
7783 {
7784   static unsigned long screen_frame_counter = 0;
7785
7786   if (mode == SCROLL_INIT)
7787   {
7788     /* set scrolling step size according to actual player's moving speed */
7789     ScrollStepSize = TILEX / player->move_delay_value;
7790
7791     screen_frame_counter = FrameCounter;
7792     ScreenMovDir = player->MovDir;
7793     ScreenMovPos = player->MovPos;
7794     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7795     return;
7796   }
7797   else if (!FrameReached(&screen_frame_counter, 1))
7798     return;
7799
7800   if (ScreenMovPos)
7801   {
7802     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7803     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7804     redraw_mask |= REDRAW_FIELD;
7805   }
7806   else
7807     ScreenMovDir = MV_NO_MOVING;
7808 }
7809
7810 void TestIfPlayerTouchesCustomElement(int x, int y)
7811 {
7812   static int xy[4][2] =
7813   {
7814     { 0, -1 },
7815     { -1, 0 },
7816     { +1, 0 },
7817     { 0, +1 }
7818   };
7819   static int change_sides[4][2] =
7820   {
7821     /* center side       border side */
7822     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7823     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7824     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7825     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7826   };
7827   static int touch_dir[4] =
7828   {
7829     MV_LEFT | MV_RIGHT,
7830     MV_UP   | MV_DOWN,
7831     MV_UP   | MV_DOWN,
7832     MV_LEFT | MV_RIGHT
7833   };
7834   int center_element = Feld[x][y];      /* should always be non-moving! */
7835   int i;
7836
7837   for (i = 0; i < 4; i++)
7838   {
7839     int xx = x + xy[i][0];
7840     int yy = y + xy[i][1];
7841     int center_side = change_sides[i][0];
7842     int border_side = change_sides[i][1];
7843     int border_element;
7844
7845     if (!IN_LEV_FIELD(xx, yy))
7846       continue;
7847
7848     if (IS_PLAYER(x, y))
7849     {
7850       if (game.engine_version < VERSION_IDENT(3,0,7,0))
7851         border_element = Feld[xx][yy];          /* may be moving! */
7852       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7853         border_element = Feld[xx][yy];
7854       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7855         border_element = MovingOrBlocked2Element(xx, yy);
7856       else
7857         continue;               /* center and border element do not touch */
7858
7859       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7860                                       CE_OTHER_GETS_TOUCHED);
7861       CheckElementSideChange(xx, yy, border_element, border_side,
7862                              CE_TOUCHED_BY_PLAYER, -1);
7863     }
7864     else if (IS_PLAYER(xx, yy))
7865     {
7866       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7867       {
7868         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7869
7870         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7871           continue;             /* center and border element do not touch */
7872       }
7873
7874       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7875                                       CE_OTHER_GETS_TOUCHED);
7876       CheckElementSideChange(x, y, center_element, center_side,
7877                              CE_TOUCHED_BY_PLAYER, -1);
7878
7879       break;
7880     }
7881   }
7882 }
7883
7884 void TestIfElementTouchesCustomElement(int x, int y)
7885 {
7886   static int xy[4][2] =
7887   {
7888     { 0, -1 },
7889     { -1, 0 },
7890     { +1, 0 },
7891     { 0, +1 }
7892   };
7893   static int change_sides[4][2] =
7894   {
7895     /* center side      border side */
7896     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7897     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7898     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7899     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7900   };
7901   static int touch_dir[4] =
7902   {
7903     MV_LEFT | MV_RIGHT,
7904     MV_UP   | MV_DOWN,
7905     MV_UP   | MV_DOWN,
7906     MV_LEFT | MV_RIGHT
7907   };
7908   boolean change_center_element = FALSE;
7909   int center_element_change_page = 0;
7910   int center_element = Feld[x][y];      /* should always be non-moving! */
7911   int i, j;
7912
7913   for (i = 0; i < 4; i++)
7914   {
7915     int xx = x + xy[i][0];
7916     int yy = y + xy[i][1];
7917     int center_side = change_sides[i][0];
7918     int border_side = change_sides[i][1];
7919     int border_element;
7920
7921     if (!IN_LEV_FIELD(xx, yy))
7922       continue;
7923
7924     if (game.engine_version < VERSION_IDENT(3,0,7,0))
7925       border_element = Feld[xx][yy];    /* may be moving! */
7926     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7927       border_element = Feld[xx][yy];
7928     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7929       border_element = MovingOrBlocked2Element(xx, yy);
7930     else
7931       continue;                 /* center and border element do not touch */
7932
7933     /* check for change of center element (but change it only once) */
7934     if (IS_CUSTOM_ELEMENT(center_element) &&
7935         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7936         !change_center_element)
7937     {
7938       for (j = 0; j < element_info[center_element].num_change_pages; j++)
7939       {
7940         struct ElementChangeInfo *change =
7941           &element_info[center_element].change_page[j];
7942
7943         if (change->can_change &&
7944             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7945             change->sides & border_side &&
7946             change->trigger_element == border_element)
7947         {
7948           change_center_element = TRUE;
7949           center_element_change_page = j;
7950
7951           break;
7952         }
7953       }
7954     }
7955
7956     /* check for change of border element */
7957     if (IS_CUSTOM_ELEMENT(border_element) &&
7958         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7959     {
7960       for (j = 0; j < element_info[border_element].num_change_pages; j++)
7961       {
7962         struct ElementChangeInfo *change =
7963           &element_info[border_element].change_page[j];
7964
7965         if (change->can_change &&
7966             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7967             change->sides & center_side &&
7968             change->trigger_element == center_element)
7969         {
7970           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7971                                  CE_OTHER_IS_TOUCHING, j);
7972           break;
7973         }
7974       }
7975     }
7976   }
7977
7978   if (change_center_element)
7979     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7980                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7981 }
7982
7983 void TestIfElementHitsCustomElement(int x, int y, int direction)
7984 {
7985   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7986   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7987   int hitx = x + dx, hity = y + dy;
7988   int hitting_element = Feld[x][y];
7989 #if 0
7990   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
7991                         !IS_FREE(hitx, hity) &&
7992                         (!IS_MOVING(hitx, hity) ||
7993                          MovDir[hitx][hity] != direction ||
7994                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
7995 #endif
7996
7997   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
7998     return;
7999
8000 #if 0
8001   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8002     return;
8003 #endif
8004
8005   CheckElementSideChange(x, y, hitting_element,
8006                          direction, CE_HITTING_SOMETHING, -1);
8007
8008   if (IN_LEV_FIELD(hitx, hity))
8009   {
8010     static int opposite_directions[] =
8011     {
8012       MV_RIGHT,
8013       MV_LEFT,
8014       MV_DOWN,
8015       MV_UP
8016     };
8017     int move_dir_bit = MV_DIR_BIT(direction);
8018     int opposite_direction = opposite_directions[move_dir_bit];
8019     int hitting_side = direction;
8020     int touched_side = opposite_direction;
8021     int touched_element = MovingOrBlocked2Element(hitx, hity);
8022 #if 1
8023     boolean object_hit = (!IS_MOVING(hitx, hity) ||
8024                           MovDir[hitx][hity] != direction ||
8025                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
8026
8027     object_hit = TRUE;
8028 #endif
8029
8030     if (object_hit)
8031     {
8032       int i;
8033
8034       CheckElementSideChange(hitx, hity, touched_element,
8035                              opposite_direction, CE_HIT_BY_SOMETHING, -1);
8036
8037       if (IS_CUSTOM_ELEMENT(hitting_element) &&
8038           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8039       {
8040         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8041         {
8042           struct ElementChangeInfo *change =
8043             &element_info[hitting_element].change_page[i];
8044
8045           if (change->can_change &&
8046               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8047               change->sides & touched_side &&
8048               change->trigger_element == touched_element)
8049           {
8050             CheckElementSideChange(x, y, hitting_element,
8051                                    CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8052             break;
8053           }
8054         }
8055       }
8056
8057       if (IS_CUSTOM_ELEMENT(touched_element) &&
8058           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8059       {
8060         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8061         {
8062           struct ElementChangeInfo *change =
8063             &element_info[touched_element].change_page[i];
8064
8065           if (change->can_change &&
8066               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8067               change->sides & hitting_side &&
8068               change->trigger_element == hitting_element)
8069           {
8070             CheckElementSideChange(hitx, hity, touched_element,
8071                                    CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8072             break;
8073           }
8074         }
8075       }
8076     }
8077   }
8078 }
8079
8080 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8081 {
8082   int i, kill_x = -1, kill_y = -1;
8083   static int test_xy[4][2] =
8084   {
8085     { 0, -1 },
8086     { -1, 0 },
8087     { +1, 0 },
8088     { 0, +1 }
8089   };
8090   static int test_dir[4] =
8091   {
8092     MV_UP,
8093     MV_LEFT,
8094     MV_RIGHT,
8095     MV_DOWN
8096   };
8097
8098   for (i = 0; i < 4; i++)
8099   {
8100     int test_x, test_y, test_move_dir, test_element;
8101
8102     test_x = good_x + test_xy[i][0];
8103     test_y = good_y + test_xy[i][1];
8104     if (!IN_LEV_FIELD(test_x, test_y))
8105       continue;
8106
8107     test_move_dir =
8108       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8109
8110 #if 0
8111     test_element = Feld[test_x][test_y];
8112 #else
8113     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8114 #endif
8115
8116     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8117        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8118     */
8119     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8120         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
8121     {
8122       kill_x = test_x;
8123       kill_y = test_y;
8124       break;
8125     }
8126   }
8127
8128   if (kill_x != -1 || kill_y != -1)
8129   {
8130     if (IS_PLAYER(good_x, good_y))
8131     {
8132       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8133
8134       if (player->shield_deadly_time_left > 0)
8135         Bang(kill_x, kill_y);
8136       else if (!PLAYER_PROTECTED(good_x, good_y))
8137         KillHero(player);
8138     }
8139     else
8140       Bang(good_x, good_y);
8141   }
8142 }
8143
8144 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8145 {
8146   int i, kill_x = -1, kill_y = -1;
8147   int bad_element = Feld[bad_x][bad_y];
8148   static int test_xy[4][2] =
8149   {
8150     { 0, -1 },
8151     { -1, 0 },
8152     { +1, 0 },
8153     { 0, +1 }
8154   };
8155   static int touch_dir[4] =
8156   {
8157     MV_LEFT | MV_RIGHT,
8158     MV_UP   | MV_DOWN,
8159     MV_UP   | MV_DOWN,
8160     MV_LEFT | MV_RIGHT
8161   };
8162   static int test_dir[4] =
8163   {
8164     MV_UP,
8165     MV_LEFT,
8166     MV_RIGHT,
8167     MV_DOWN
8168   };
8169
8170   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
8171     return;
8172
8173   for (i = 0; i < 4; i++)
8174   {
8175     int test_x, test_y, test_move_dir, test_element;
8176
8177     test_x = bad_x + test_xy[i][0];
8178     test_y = bad_y + test_xy[i][1];
8179     if (!IN_LEV_FIELD(test_x, test_y))
8180       continue;
8181
8182     test_move_dir =
8183       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8184
8185     test_element = Feld[test_x][test_y];
8186
8187     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8188        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8189     */
8190     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
8191         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
8192     {
8193       /* good thing is player or penguin that does not move away */
8194       if (IS_PLAYER(test_x, test_y))
8195       {
8196         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8197
8198         if (bad_element == EL_ROBOT && player->is_moving)
8199           continue;     /* robot does not kill player if he is moving */
8200
8201         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8202         {
8203           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8204             continue;           /* center and border element do not touch */
8205         }
8206
8207         kill_x = test_x;
8208         kill_y = test_y;
8209         break;
8210       }
8211       else if (test_element == EL_PENGUIN)
8212       {
8213         kill_x = test_x;
8214         kill_y = test_y;
8215         break;
8216       }
8217     }
8218   }
8219
8220   if (kill_x != -1 || kill_y != -1)
8221   {
8222     if (IS_PLAYER(kill_x, kill_y))
8223     {
8224       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8225
8226       if (player->shield_deadly_time_left > 0)
8227         Bang(bad_x, bad_y);
8228       else if (!PLAYER_PROTECTED(kill_x, kill_y))
8229         KillHero(player);
8230     }
8231     else
8232       Bang(kill_x, kill_y);
8233   }
8234 }
8235
8236 void TestIfHeroTouchesBadThing(int x, int y)
8237 {
8238   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8239 }
8240
8241 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8242 {
8243   TestIfGoodThingHitsBadThing(x, y, move_dir);
8244 }
8245
8246 void TestIfBadThingTouchesHero(int x, int y)
8247 {
8248   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8249 }
8250
8251 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8252 {
8253   TestIfBadThingHitsGoodThing(x, y, move_dir);
8254 }
8255
8256 void TestIfFriendTouchesBadThing(int x, int y)
8257 {
8258   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8259 }
8260
8261 void TestIfBadThingTouchesFriend(int x, int y)
8262 {
8263   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8264 }
8265
8266 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8267 {
8268   int i, kill_x = bad_x, kill_y = bad_y;
8269   static int xy[4][2] =
8270   {
8271     { 0, -1 },
8272     { -1, 0 },
8273     { +1, 0 },
8274     { 0, +1 }
8275   };
8276
8277   for (i = 0; i < 4; i++)
8278   {
8279     int x, y, element;
8280
8281     x = bad_x + xy[i][0];
8282     y = bad_y + xy[i][1];
8283     if (!IN_LEV_FIELD(x, y))
8284       continue;
8285
8286     element = Feld[x][y];
8287     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8288         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8289     {
8290       kill_x = x;
8291       kill_y = y;
8292       break;
8293     }
8294   }
8295
8296   if (kill_x != bad_x || kill_y != bad_y)
8297     Bang(bad_x, bad_y);
8298 }
8299
8300 void KillHero(struct PlayerInfo *player)
8301 {
8302   int jx = player->jx, jy = player->jy;
8303
8304   if (!player->active)
8305     return;
8306
8307   /* remove accessible field at the player's position */
8308   Feld[jx][jy] = EL_EMPTY;
8309
8310   /* deactivate shield (else Bang()/Explode() would not work right) */
8311   player->shield_normal_time_left = 0;
8312   player->shield_deadly_time_left = 0;
8313
8314   Bang(jx, jy);
8315   BuryHero(player);
8316 }
8317
8318 static void KillHeroUnlessProtected(int x, int y)
8319 {
8320   if (!PLAYER_PROTECTED(x, y))
8321     KillHero(PLAYERINFO(x, y));
8322 }
8323
8324 void BuryHero(struct PlayerInfo *player)
8325 {
8326   int jx = player->jx, jy = player->jy;
8327
8328   if (!player->active)
8329     return;
8330
8331 #if 1
8332   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8333 #else
8334   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8335 #endif
8336   PlayLevelSound(jx, jy, SND_GAME_LOSING);
8337
8338   player->GameOver = TRUE;
8339   RemoveHero(player);
8340 }
8341
8342 void RemoveHero(struct PlayerInfo *player)
8343 {
8344   int jx = player->jx, jy = player->jy;
8345   int i, found = FALSE;
8346
8347   player->present = FALSE;
8348   player->active = FALSE;
8349
8350   if (!ExplodeField[jx][jy])
8351     StorePlayer[jx][jy] = 0;
8352
8353   for (i = 0; i < MAX_PLAYERS; i++)
8354     if (stored_player[i].active)
8355       found = TRUE;
8356
8357   if (!found)
8358     AllPlayersGone = TRUE;
8359
8360   ExitX = ZX = jx;
8361   ExitY = ZY = jy;
8362 }
8363
8364 /*
8365   =============================================================================
8366   checkDiagonalPushing()
8367   -----------------------------------------------------------------------------
8368   check if diagonal input device direction results in pushing of object
8369   (by checking if the alternative direction is walkable, diggable, ...)
8370   =============================================================================
8371 */
8372
8373 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8374                                     int x, int y, int real_dx, int real_dy)
8375 {
8376   int jx, jy, dx, dy, xx, yy;
8377
8378   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
8379     return TRUE;
8380
8381   /* diagonal direction: check alternative direction */
8382   jx = player->jx;
8383   jy = player->jy;
8384   dx = x - jx;
8385   dy = y - jy;
8386   xx = jx + (dx == 0 ? real_dx : 0);
8387   yy = jy + (dy == 0 ? real_dy : 0);
8388
8389   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8390 }
8391
8392 /*
8393   =============================================================================
8394   DigField()
8395   -----------------------------------------------------------------------------
8396   x, y:                 field next to player (non-diagonal) to try to dig to
8397   real_dx, real_dy:     direction as read from input device (can be diagonal)
8398   =============================================================================
8399 */
8400
8401 int DigField(struct PlayerInfo *player,
8402              int x, int y, int real_dx, int real_dy, int mode)
8403 {
8404   static int change_sides[4] =
8405   {
8406     CH_SIDE_RIGHT,      /* moving left  */
8407     CH_SIDE_LEFT,       /* moving right */
8408     CH_SIDE_BOTTOM,     /* moving up    */
8409     CH_SIDE_TOP,        /* moving down  */
8410   };
8411   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8412   int jx = player->jx, jy = player->jy;
8413   int dx = x - jx, dy = y - jy;
8414   int nextx = x + dx, nexty = y + dy;
8415   int move_direction = (dx == -1 ? MV_LEFT :
8416                         dx == +1 ? MV_RIGHT :
8417                         dy == -1 ? MV_UP :
8418                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8419   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8420   int element;
8421
8422   if (player->MovPos == 0)
8423   {
8424     player->is_digging = FALSE;
8425     player->is_collecting = FALSE;
8426   }
8427
8428   if (player->MovPos == 0)      /* last pushing move finished */
8429     player->is_pushing = FALSE;
8430
8431   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
8432   {
8433     player->is_switching = FALSE;
8434     player->push_delay = 0;
8435
8436     return MF_NO_ACTION;
8437   }
8438
8439   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8440     return MF_NO_ACTION;
8441
8442 #if 0
8443   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8444 #else
8445   if (IS_TUBE(Feld[jx][jy]) ||
8446       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8447 #endif
8448   {
8449     int i = 0;
8450     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8451     int tube_leave_directions[][2] =
8452     {
8453       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8454       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
8455       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
8456       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
8457       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
8458       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
8459       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
8460       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
8461       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
8462       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
8463       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
8464       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8465     };
8466
8467     while (tube_leave_directions[i][0] != tube_element)
8468     {
8469       i++;
8470       if (tube_leave_directions[i][0] == -1)    /* should not happen */
8471         break;
8472     }
8473
8474     if (!(tube_leave_directions[i][1] & move_direction))
8475       return MF_NO_ACTION;      /* tube has no opening in this direction */
8476   }
8477
8478   element = Feld[x][y];
8479
8480   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8481       game.engine_version >= VERSION_IDENT(2,2,0,0))
8482     return MF_NO_ACTION;
8483
8484   switch (element)
8485   {
8486     case EL_SP_PORT_LEFT:
8487     case EL_SP_PORT_RIGHT:
8488     case EL_SP_PORT_UP:
8489     case EL_SP_PORT_DOWN:
8490     case EL_SP_PORT_HORIZONTAL:
8491     case EL_SP_PORT_VERTICAL:
8492     case EL_SP_PORT_ANY:
8493     case EL_SP_GRAVITY_PORT_LEFT:
8494     case EL_SP_GRAVITY_PORT_RIGHT:
8495     case EL_SP_GRAVITY_PORT_UP:
8496     case EL_SP_GRAVITY_PORT_DOWN:
8497       if ((dx == -1 &&
8498            element != EL_SP_PORT_LEFT &&
8499            element != EL_SP_GRAVITY_PORT_LEFT &&
8500            element != EL_SP_PORT_HORIZONTAL &&
8501            element != EL_SP_PORT_ANY) ||
8502           (dx == +1 &&
8503            element != EL_SP_PORT_RIGHT &&
8504            element != EL_SP_GRAVITY_PORT_RIGHT &&
8505            element != EL_SP_PORT_HORIZONTAL &&
8506            element != EL_SP_PORT_ANY) ||
8507           (dy == -1 &&
8508            element != EL_SP_PORT_UP &&
8509            element != EL_SP_GRAVITY_PORT_UP &&
8510            element != EL_SP_PORT_VERTICAL &&
8511            element != EL_SP_PORT_ANY) ||
8512           (dy == +1 &&
8513            element != EL_SP_PORT_DOWN &&
8514            element != EL_SP_GRAVITY_PORT_DOWN &&
8515            element != EL_SP_PORT_VERTICAL &&
8516            element != EL_SP_PORT_ANY) ||
8517           !IN_LEV_FIELD(nextx, nexty) ||
8518           !IS_FREE(nextx, nexty))
8519         return MF_NO_ACTION;
8520
8521       if (element == EL_SP_GRAVITY_PORT_LEFT ||
8522           element == EL_SP_GRAVITY_PORT_RIGHT ||
8523           element == EL_SP_GRAVITY_PORT_UP ||
8524           element == EL_SP_GRAVITY_PORT_DOWN)
8525         game.gravity = !game.gravity;
8526
8527       /* automatically move to the next field with double speed */
8528       player->programmed_action = move_direction;
8529 #if 0
8530       if (player->move_delay_reset_counter == 0)
8531       {
8532         player->move_delay_reset_counter = 2;   /* two double speed steps */
8533
8534         DOUBLE_PLAYER_SPEED(player);
8535       }
8536 #else
8537       DOUBLE_PLAYER_SPEED(player);
8538
8539       player->move_delay_reset_counter = 2;
8540 #endif
8541
8542       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8543       break;
8544
8545     case EL_TUBE_ANY:
8546     case EL_TUBE_VERTICAL:
8547     case EL_TUBE_HORIZONTAL:
8548     case EL_TUBE_VERTICAL_LEFT:
8549     case EL_TUBE_VERTICAL_RIGHT:
8550     case EL_TUBE_HORIZONTAL_UP:
8551     case EL_TUBE_HORIZONTAL_DOWN:
8552     case EL_TUBE_LEFT_UP:
8553     case EL_TUBE_LEFT_DOWN:
8554     case EL_TUBE_RIGHT_UP:
8555     case EL_TUBE_RIGHT_DOWN:
8556       {
8557         int i = 0;
8558         int tube_enter_directions[][2] =
8559         {
8560           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8561           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
8562           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
8563           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
8564           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
8565           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
8566           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
8567           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
8568           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
8569           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
8570           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
8571           { -1,                         MV_NO_MOVING                         }
8572         };
8573
8574         while (tube_enter_directions[i][0] != element)
8575         {
8576           i++;
8577           if (tube_enter_directions[i][0] == -1)        /* should not happen */
8578             break;
8579         }
8580
8581         if (!(tube_enter_directions[i][1] & move_direction))
8582           return MF_NO_ACTION;  /* tube has no opening in this direction */
8583
8584         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8585       }
8586       break;
8587
8588     default:
8589
8590       if (IS_WALKABLE(element))
8591       {
8592         int sound_action = ACTION_WALKING;
8593
8594         if (element >= EL_GATE_1 && element <= EL_GATE_4)
8595         {
8596           if (!player->key[element - EL_GATE_1])
8597             return MF_NO_ACTION;
8598         }
8599         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8600         {
8601           if (!player->key[element - EL_GATE_1_GRAY])
8602             return MF_NO_ACTION;
8603         }
8604         else if (element == EL_EXIT_OPEN ||
8605                  element == EL_SP_EXIT_OPEN ||
8606                  element == EL_SP_EXIT_OPENING)
8607         {
8608           sound_action = ACTION_PASSING;        /* player is passing exit */
8609         }
8610         else if (element == EL_EMPTY)
8611         {
8612           sound_action = ACTION_MOVING;         /* nothing to walk on */
8613         }
8614
8615         /* play sound from background or player, whatever is available */
8616         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8617           PlayLevelSoundElementAction(x, y, element, sound_action);
8618         else
8619           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8620
8621         break;
8622       }
8623       else if (IS_PASSABLE(element))
8624       {
8625         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8626           return MF_NO_ACTION;
8627
8628 #if 1
8629         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
8630           return MF_NO_ACTION;
8631 #endif
8632
8633         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8634         {
8635           if (!player->key[element - EL_EM_GATE_1])
8636             return MF_NO_ACTION;
8637         }
8638         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8639         {
8640           if (!player->key[element - EL_EM_GATE_1_GRAY])
8641             return MF_NO_ACTION;
8642         }
8643
8644         /* automatically move to the next field with double speed */
8645         player->programmed_action = move_direction;
8646 #if 1
8647         if (player->move_delay_reset_counter == 0)
8648         {
8649           player->move_delay_reset_counter = 2; /* two double speed steps */
8650
8651           DOUBLE_PLAYER_SPEED(player);
8652         }
8653 #else
8654         DOUBLE_PLAYER_SPEED(player);
8655
8656         player->move_delay_reset_counter = 2;
8657 #endif
8658
8659         PlayLevelSoundAction(x, y, ACTION_PASSING);
8660
8661         break;
8662       }
8663       else if (IS_DIGGABLE(element))
8664       {
8665         RemoveField(x, y);
8666
8667         if (mode != DF_SNAP)
8668         {
8669 #if 1
8670           GfxElement[x][y] = GFX_ELEMENT(element);
8671 #else
8672           GfxElement[x][y] =
8673             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8674 #endif
8675           player->is_digging = TRUE;
8676         }
8677
8678         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8679
8680         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8681
8682 #if 1
8683         if (mode == DF_SNAP)
8684           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8685 #endif
8686
8687         break;
8688       }
8689       else if (IS_COLLECTIBLE(element))
8690       {
8691         RemoveField(x, y);
8692
8693         if (mode != DF_SNAP)
8694         {
8695           GfxElement[x][y] = element;
8696           player->is_collecting = TRUE;
8697         }
8698
8699         if (element == EL_SPEED_PILL)
8700           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8701         else if (element == EL_EXTRA_TIME && level.time > 0)
8702         {
8703           TimeLeft += 10;
8704           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8705         }
8706         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8707         {
8708           player->shield_normal_time_left += 10;
8709           if (element == EL_SHIELD_DEADLY)
8710             player->shield_deadly_time_left += 10;
8711         }
8712         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8713         {
8714           if (player->inventory_size < MAX_INVENTORY_SIZE)
8715             player->inventory_element[player->inventory_size++] = element;
8716
8717           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8718                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8719         }
8720         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8721         {
8722           player->dynabomb_count++;
8723           player->dynabombs_left++;
8724         }
8725         else if (element == EL_DYNABOMB_INCREASE_SIZE)
8726         {
8727           player->dynabomb_size++;
8728         }
8729         else if (element == EL_DYNABOMB_INCREASE_POWER)
8730         {
8731           player->dynabomb_xl = TRUE;
8732         }
8733         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8734                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8735         {
8736           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8737                         element - EL_KEY_1 : element - EL_EM_KEY_1);
8738
8739           player->key[key_nr] = TRUE;
8740
8741           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8742                              el2edimg(EL_KEY_1 + key_nr));
8743           redraw_mask |= REDRAW_DOOR_1;
8744         }
8745         else if (IS_ENVELOPE(element))
8746         {
8747 #if 1
8748           player->show_envelope = element;
8749 #else
8750           ShowEnvelope(element - EL_ENVELOPE_1);
8751 #endif
8752         }
8753         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8754         {
8755           int i;
8756
8757           for (i = 0; i < element_info[element].collect_count; i++)
8758             if (player->inventory_size < MAX_INVENTORY_SIZE)
8759               player->inventory_element[player->inventory_size++] = element;
8760
8761           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8762                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8763         }
8764         else if (element_info[element].collect_count > 0)
8765         {
8766           local_player->gems_still_needed -=
8767             element_info[element].collect_count;
8768           if (local_player->gems_still_needed < 0)
8769             local_player->gems_still_needed = 0;
8770
8771           DrawText(DX_EMERALDS, DY_EMERALDS,
8772                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8773         }
8774
8775         RaiseScoreElement(element);
8776         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8777
8778         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8779
8780 #if 1
8781         if (mode == DF_SNAP)
8782           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8783 #endif
8784
8785         break;
8786       }
8787       else if (IS_PUSHABLE(element))
8788       {
8789         if (mode == DF_SNAP && element != EL_BD_ROCK)
8790           return MF_NO_ACTION;
8791
8792         if (CAN_FALL(element) && dy)
8793           return MF_NO_ACTION;
8794
8795         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8796             !(element == EL_SPRING && use_spring_bug))
8797           return MF_NO_ACTION;
8798
8799 #if 1
8800         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8801             ((move_direction & MV_VERTICAL &&
8802               ((element_info[element].move_pattern & MV_LEFT &&
8803                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8804                (element_info[element].move_pattern & MV_RIGHT &&
8805                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8806              (move_direction & MV_HORIZONTAL &&
8807               ((element_info[element].move_pattern & MV_UP &&
8808                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8809                (element_info[element].move_pattern & MV_DOWN &&
8810                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8811           return MF_NO_ACTION;
8812 #endif
8813
8814 #if 1
8815         /* do not push elements already moving away faster than player */
8816         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8817             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8818           return MF_NO_ACTION;
8819 #else
8820         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8821           return MF_NO_ACTION;
8822 #endif
8823
8824 #if 1
8825         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8826         {
8827           if (player->push_delay_value == -1)
8828             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8829         }
8830         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8831         {
8832           if (!player->is_pushing)
8833             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8834         }
8835
8836         /*
8837         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8838             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8839              !player_is_pushing))
8840           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8841         */
8842 #else
8843         if (!player->is_pushing &&
8844             game.engine_version >= VERSION_IDENT(2,2,0,7))
8845           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8846 #endif
8847
8848 #if 0
8849         printf("::: push delay: %ld [%d, %d] [%d]\n",
8850                player->push_delay_value, FrameCounter, game.engine_version,
8851                player->is_pushing);
8852 #endif
8853
8854         player->is_pushing = TRUE;
8855
8856         if (!(IN_LEV_FIELD(nextx, nexty) &&
8857               (IS_FREE(nextx, nexty) ||
8858                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8859                 IS_SB_ELEMENT(element)))))
8860           return MF_NO_ACTION;
8861
8862         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8863           return MF_NO_ACTION;
8864
8865         if (player->push_delay == 0)    /* new pushing; restart delay */
8866           player->push_delay = FrameCounter;
8867
8868         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8869             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8870             element != EL_SPRING && element != EL_BALLOON)
8871         {
8872           /* make sure that there is no move delay before next try to push */
8873           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8874             player->move_delay = INITIAL_MOVE_DELAY_OFF;
8875
8876           return MF_NO_ACTION;
8877         }
8878
8879 #if 0
8880         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8881 #endif
8882
8883         if (IS_SB_ELEMENT(element))
8884         {
8885           if (element == EL_SOKOBAN_FIELD_FULL)
8886           {
8887             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8888             local_player->sokobanfields_still_needed++;
8889           }
8890
8891           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8892           {
8893             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8894             local_player->sokobanfields_still_needed--;
8895           }
8896
8897           Feld[x][y] = EL_SOKOBAN_OBJECT;
8898
8899           if (Back[x][y] == Back[nextx][nexty])
8900             PlayLevelSoundAction(x, y, ACTION_PUSHING);
8901           else if (Back[x][y] != 0)
8902             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8903                                         ACTION_EMPTYING);
8904           else
8905             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8906                                         ACTION_FILLING);
8907
8908           if (local_player->sokobanfields_still_needed == 0 &&
8909               game.emulation == EMU_SOKOBAN)
8910           {
8911             player->LevelSolved = player->GameOver = TRUE;
8912             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8913           }
8914         }
8915         else
8916           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8917
8918         InitMovingField(x, y, move_direction);
8919         GfxAction[x][y] = ACTION_PUSHING;
8920
8921         if (mode == DF_SNAP)
8922           ContinueMoving(x, y);
8923         else
8924           MovPos[x][y] = (dx != 0 ? dx : dy);
8925
8926         Pushed[x][y] = TRUE;
8927         Pushed[nextx][nexty] = TRUE;
8928
8929         if (game.engine_version < VERSION_IDENT(2,2,0,7))
8930           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8931         else
8932           player->push_delay_value = -1;        /* get new value later */
8933
8934         CheckTriggeredElementSideChange(x, y, element, dig_side,
8935                                         CE_OTHER_GETS_PUSHED);
8936         CheckElementSideChange(x, y, element, dig_side,
8937                                CE_PUSHED_BY_PLAYER, -1);
8938
8939         break;
8940       }
8941       else if (IS_SWITCHABLE(element))
8942       {
8943         if (PLAYER_SWITCHING(player, x, y))
8944           return MF_ACTION;
8945
8946         player->is_switching = TRUE;
8947         player->switch_x = x;
8948         player->switch_y = y;
8949
8950         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8951
8952         if (element == EL_ROBOT_WHEEL)
8953         {
8954           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8955           ZX = x;
8956           ZY = y;
8957
8958           DrawLevelField(x, y);
8959         }
8960         else if (element == EL_SP_TERMINAL)
8961         {
8962           int xx, yy;
8963
8964           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8965           {
8966             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8967               Bang(xx, yy);
8968             else if (Feld[xx][yy] == EL_SP_TERMINAL)
8969               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8970           }
8971         }
8972         else if (IS_BELT_SWITCH(element))
8973         {
8974           ToggleBeltSwitch(x, y);
8975         }
8976         else if (element == EL_SWITCHGATE_SWITCH_UP ||
8977                  element == EL_SWITCHGATE_SWITCH_DOWN)
8978         {
8979           ToggleSwitchgateSwitch(x, y);
8980         }
8981         else if (element == EL_LIGHT_SWITCH ||
8982                  element == EL_LIGHT_SWITCH_ACTIVE)
8983         {
8984           ToggleLightSwitch(x, y);
8985
8986 #if 0
8987           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8988                          SND_LIGHT_SWITCH_ACTIVATING :
8989                          SND_LIGHT_SWITCH_DEACTIVATING);
8990 #endif
8991         }
8992         else if (element == EL_TIMEGATE_SWITCH)
8993         {
8994           ActivateTimegateSwitch(x, y);
8995         }
8996         else if (element == EL_BALLOON_SWITCH_LEFT ||
8997                  element == EL_BALLOON_SWITCH_RIGHT ||
8998                  element == EL_BALLOON_SWITCH_UP ||
8999                  element == EL_BALLOON_SWITCH_DOWN ||
9000                  element == EL_BALLOON_SWITCH_ANY)
9001         {
9002           if (element == EL_BALLOON_SWITCH_ANY)
9003             game.balloon_dir = move_direction;
9004           else
9005             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9006                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9007                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9008                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9009                                 MV_NO_MOVING);
9010         }
9011         else if (element == EL_LAMP)
9012         {
9013           Feld[x][y] = EL_LAMP_ACTIVE;
9014           local_player->lights_still_needed--;
9015
9016           DrawLevelField(x, y);
9017         }
9018         else if (element == EL_TIME_ORB_FULL)
9019         {
9020           Feld[x][y] = EL_TIME_ORB_EMPTY;
9021           TimeLeft += 10;
9022           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9023
9024           DrawLevelField(x, y);
9025
9026 #if 0
9027           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9028 #endif
9029         }
9030
9031         return MF_ACTION;
9032       }
9033       else
9034       {
9035         if (!PLAYER_SWITCHING(player, x, y))
9036         {
9037           player->is_switching = TRUE;
9038           player->switch_x = x;
9039           player->switch_y = y;
9040
9041           CheckTriggeredElementSideChange(x, y, element, dig_side,
9042                                           CE_OTHER_IS_SWITCHING);
9043           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9044         }
9045
9046         CheckTriggeredElementSideChange(x, y, element, dig_side,
9047                                         CE_OTHER_GETS_PRESSED);
9048         CheckElementSideChange(x, y, element, dig_side,
9049                                CE_PRESSED_BY_PLAYER, -1);
9050       }
9051
9052       return MF_NO_ACTION;
9053   }
9054
9055   player->push_delay = 0;
9056
9057   if (Feld[x][y] != element)            /* really digged/collected something */
9058     player->is_collecting = !player->is_digging;
9059
9060   return MF_MOVING;
9061 }
9062
9063 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9064 {
9065   int jx = player->jx, jy = player->jy;
9066   int x = jx + dx, y = jy + dy;
9067   int snap_direction = (dx == -1 ? MV_LEFT :
9068                         dx == +1 ? MV_RIGHT :
9069                         dy == -1 ? MV_UP :
9070                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9071
9072   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9073     return FALSE;
9074
9075   if (!player->active || !IN_LEV_FIELD(x, y))
9076     return FALSE;
9077
9078   if (dx && dy)
9079     return FALSE;
9080
9081   if (!dx && !dy)
9082   {
9083     if (player->MovPos == 0)
9084       player->is_pushing = FALSE;
9085
9086     player->is_snapping = FALSE;
9087
9088     if (player->MovPos == 0)
9089     {
9090       player->is_moving = FALSE;
9091       player->is_digging = FALSE;
9092       player->is_collecting = FALSE;
9093     }
9094
9095     return FALSE;
9096   }
9097
9098   if (player->is_snapping)
9099     return FALSE;
9100
9101   player->MovDir = snap_direction;
9102
9103   player->is_moving = FALSE;
9104   player->is_digging = FALSE;
9105   player->is_collecting = FALSE;
9106
9107   player->is_dropping = FALSE;
9108
9109   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9110     return FALSE;
9111
9112   player->is_snapping = TRUE;
9113
9114   player->is_moving = FALSE;
9115   player->is_digging = FALSE;
9116   player->is_collecting = FALSE;
9117
9118   DrawLevelField(x, y);
9119   BackToFront();
9120
9121   return TRUE;
9122 }
9123
9124 boolean DropElement(struct PlayerInfo *player)
9125 {
9126   int jx = player->jx, jy = player->jy;
9127   int old_element = Feld[jx][jy];
9128   int new_element;
9129
9130   /* check if player is active, not moving and ready to drop */
9131   if (!player->active || player->MovPos || player->drop_delay > 0)
9132     return FALSE;
9133
9134   /* check if player has anything that can be dropped */
9135   if (player->inventory_size == 0 && player->dynabombs_left == 0)
9136     return FALSE;
9137
9138   /* check if anything can be dropped at the current position */
9139   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9140     return FALSE;
9141
9142   /* collected custom elements can only be dropped on empty fields */
9143   if (player->inventory_size > 0 &&
9144       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9145       && old_element != EL_EMPTY)
9146     return FALSE;
9147
9148   if (old_element != EL_EMPTY)
9149     Back[jx][jy] = old_element;         /* store old element on this field */
9150
9151   ResetGfxAnimation(jx, jy);
9152   ResetRandomAnimationValue(jx, jy);
9153
9154   if (player->inventory_size > 0)
9155   {
9156     player->inventory_size--;
9157     new_element = player->inventory_element[player->inventory_size];
9158
9159     if (new_element == EL_DYNAMITE)
9160       new_element = EL_DYNAMITE_ACTIVE;
9161     else if (new_element == EL_SP_DISK_RED)
9162       new_element = EL_SP_DISK_RED_ACTIVE;
9163
9164     Feld[jx][jy] = new_element;
9165
9166     DrawText(DX_DYNAMITE, DY_DYNAMITE,
9167              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9168
9169     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9170       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9171
9172     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9173
9174     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9175     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9176
9177     TestIfElementTouchesCustomElement(jx, jy);
9178   }
9179   else          /* player is dropping a dyna bomb */
9180   {
9181     player->dynabombs_left--;
9182     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9183
9184     Feld[jx][jy] = new_element;
9185
9186     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9187       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9188
9189     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9190   }
9191
9192
9193
9194 #if 1
9195
9196   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
9197   {
9198     InitField(jx, jy, FALSE);
9199     if (CAN_MOVE(Feld[jx][jy]))
9200       InitMovDir(jx, jy);
9201   }
9202
9203   new_element = Feld[jx][jy];
9204
9205   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9206       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9207   {
9208     int move_stepsize = element_info[new_element].move_stepsize;
9209     int direction, dx, dy, nextx, nexty;
9210
9211     if (element_info[new_element].move_direction_initial == MV_NO_MOVING)
9212       MovDir[jx][jy] = player->MovDir;
9213
9214     direction = MovDir[jx][jy];
9215     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9216     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9217     nextx = jx + dx;
9218     nexty = jy + dy;
9219
9220     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9221     {
9222 #if 0
9223       WasJustMoving[jx][jy] = 3;
9224 #else
9225       InitMovingField(jx, jy, direction);
9226       ContinueMoving(jx, jy);
9227 #endif
9228     }
9229     else
9230     {
9231       Changed[jx][jy] = 0;            /* allow another change */
9232
9233 #if 1
9234       TestIfElementHitsCustomElement(jx, jy, direction);
9235 #else
9236       CheckElementSideChange(jx, jy, new_element,
9237                              direction, CE_HITTING_SOMETHING, -1);
9238 #endif
9239     }
9240
9241     player->drop_delay = 2 * TILEX / move_stepsize + 1;
9242   }
9243
9244 #if 0
9245   player->drop_delay = 8 + 8 + 8;
9246 #endif
9247
9248 #endif
9249
9250   player->is_dropping = TRUE;
9251
9252
9253   return TRUE;
9254 }
9255
9256 /* ------------------------------------------------------------------------- */
9257 /* game sound playing functions                                              */
9258 /* ------------------------------------------------------------------------- */
9259
9260 static int *loop_sound_frame = NULL;
9261 static int *loop_sound_volume = NULL;
9262
9263 void InitPlayLevelSound()
9264 {
9265   int num_sounds = getSoundListSize();
9266
9267   checked_free(loop_sound_frame);
9268   checked_free(loop_sound_volume);
9269
9270   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
9271   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9272 }
9273
9274 static void PlayLevelSound(int x, int y, int nr)
9275 {
9276   int sx = SCREENX(x), sy = SCREENY(y);
9277   int volume, stereo_position;
9278   int max_distance = 8;
9279   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9280
9281   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9282       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9283     return;
9284
9285   if (!IN_LEV_FIELD(x, y) ||
9286       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9287       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9288     return;
9289
9290   volume = SOUND_MAX_VOLUME;
9291
9292   if (!IN_SCR_FIELD(sx, sy))
9293   {
9294     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9295     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9296
9297     volume -= volume * (dx > dy ? dx : dy) / max_distance;
9298   }
9299
9300   stereo_position = (SOUND_MAX_LEFT +
9301                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9302                      (SCR_FIELDX + 2 * max_distance));
9303
9304   if (IS_LOOP_SOUND(nr))
9305   {
9306     /* This assures that quieter loop sounds do not overwrite louder ones,
9307        while restarting sound volume comparison with each new game frame. */
9308
9309     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9310       return;
9311
9312     loop_sound_volume[nr] = volume;
9313     loop_sound_frame[nr] = FrameCounter;
9314   }
9315
9316   PlaySoundExt(nr, volume, stereo_position, type);
9317 }
9318
9319 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9320 {
9321   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9322                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
9323                  y < LEVELY(BY1) ? LEVELY(BY1) :
9324                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
9325                  sound_action);
9326 }
9327
9328 static void PlayLevelSoundAction(int x, int y, int action)
9329 {
9330   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9331 }
9332
9333 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9334 {
9335   int sound_effect = element_info[element].sound[action];
9336
9337   if (sound_effect != SND_UNDEFINED)
9338     PlayLevelSound(x, y, sound_effect);
9339 }
9340
9341 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9342                                               int action)
9343 {
9344   int sound_effect = element_info[element].sound[action];
9345
9346   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9347     PlayLevelSound(x, y, sound_effect);
9348 }
9349
9350 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9351 {
9352   int sound_effect = element_info[Feld[x][y]].sound[action];
9353
9354   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9355     PlayLevelSound(x, y, sound_effect);
9356 }
9357
9358 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9359 {
9360   int sound_effect = element_info[Feld[x][y]].sound[action];
9361
9362   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9363     StopSound(sound_effect);
9364 }
9365
9366 static void PlayLevelMusic()
9367 {
9368   if (levelset.music[level_nr] != MUS_UNDEFINED)
9369     PlayMusic(levelset.music[level_nr]);        /* from config file */
9370   else
9371     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
9372 }
9373
9374 void RaiseScore(int value)
9375 {
9376   local_player->score += value;
9377   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9378 }
9379
9380 void RaiseScoreElement(int element)
9381 {
9382   switch(element)
9383   {
9384     case EL_EMERALD:
9385     case EL_BD_DIAMOND:
9386     case EL_EMERALD_YELLOW:
9387     case EL_EMERALD_RED:
9388     case EL_EMERALD_PURPLE:
9389     case EL_SP_INFOTRON:
9390       RaiseScore(level.score[SC_EMERALD]);
9391       break;
9392     case EL_DIAMOND:
9393       RaiseScore(level.score[SC_DIAMOND]);
9394       break;
9395     case EL_CRYSTAL:
9396       RaiseScore(level.score[SC_CRYSTAL]);
9397       break;
9398     case EL_PEARL:
9399       RaiseScore(level.score[SC_PEARL]);
9400       break;
9401     case EL_BUG:
9402     case EL_BD_BUTTERFLY:
9403     case EL_SP_ELECTRON:
9404       RaiseScore(level.score[SC_BUG]);
9405       break;
9406     case EL_SPACESHIP:
9407     case EL_BD_FIREFLY:
9408     case EL_SP_SNIKSNAK:
9409       RaiseScore(level.score[SC_SPACESHIP]);
9410       break;
9411     case EL_YAMYAM:
9412     case EL_DARK_YAMYAM:
9413       RaiseScore(level.score[SC_YAMYAM]);
9414       break;
9415     case EL_ROBOT:
9416       RaiseScore(level.score[SC_ROBOT]);
9417       break;
9418     case EL_PACMAN:
9419       RaiseScore(level.score[SC_PACMAN]);
9420       break;
9421     case EL_NUT:
9422       RaiseScore(level.score[SC_NUT]);
9423       break;
9424     case EL_DYNAMITE:
9425     case EL_SP_DISK_RED:
9426     case EL_DYNABOMB_INCREASE_NUMBER:
9427     case EL_DYNABOMB_INCREASE_SIZE:
9428     case EL_DYNABOMB_INCREASE_POWER:
9429       RaiseScore(level.score[SC_DYNAMITE]);
9430       break;
9431     case EL_SHIELD_NORMAL:
9432     case EL_SHIELD_DEADLY:
9433       RaiseScore(level.score[SC_SHIELD]);
9434       break;
9435     case EL_EXTRA_TIME:
9436       RaiseScore(level.score[SC_TIME_BONUS]);
9437       break;
9438     case EL_KEY_1:
9439     case EL_KEY_2:
9440     case EL_KEY_3:
9441     case EL_KEY_4:
9442       RaiseScore(level.score[SC_KEY]);
9443       break;
9444     default:
9445       RaiseScore(element_info[element].collect_score);
9446       break;
9447   }
9448 }
9449
9450 void RequestQuitGame(boolean ask_if_really_quit)
9451 {
9452   if (AllPlayersGone ||
9453       !ask_if_really_quit ||
9454       level_editor_test_game ||
9455       Request("Do you really want to quit the game ?",
9456               REQ_ASK | REQ_STAY_CLOSED))
9457   {
9458 #if defined(PLATFORM_UNIX)
9459     if (options.network)
9460       SendToServer_StopPlaying();
9461     else
9462 #endif
9463     {
9464       game_status = GAME_MODE_MAIN;
9465       DrawMainMenu();
9466     }
9467   }
9468   else
9469   {
9470     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9471   }
9472 }
9473
9474
9475 /* ---------- new game button stuff ---------------------------------------- */
9476
9477 /* graphic position values for game buttons */
9478 #define GAME_BUTTON_XSIZE       30
9479 #define GAME_BUTTON_YSIZE       30
9480 #define GAME_BUTTON_XPOS        5
9481 #define GAME_BUTTON_YPOS        215
9482 #define SOUND_BUTTON_XPOS       5
9483 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9484
9485 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9486 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9487 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9488 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9489 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9490 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9491
9492 static struct
9493 {
9494   int x, y;
9495   int gadget_id;
9496   char *infotext;
9497 } gamebutton_info[NUM_GAME_BUTTONS] =
9498 {
9499   {
9500     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
9501     GAME_CTRL_ID_STOP,
9502     "stop game"
9503   },
9504   {
9505     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
9506     GAME_CTRL_ID_PAUSE,
9507     "pause game"
9508   },
9509   {
9510     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
9511     GAME_CTRL_ID_PLAY,
9512     "play game"
9513   },
9514   {
9515     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
9516     SOUND_CTRL_ID_MUSIC,
9517     "background music on/off"
9518   },
9519   {
9520     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
9521     SOUND_CTRL_ID_LOOPS,
9522     "sound loops on/off"
9523   },
9524   {
9525     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
9526     SOUND_CTRL_ID_SIMPLE,
9527     "normal sounds on/off"
9528   }
9529 };
9530
9531 void CreateGameButtons()
9532 {
9533   int i;
9534
9535   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9536   {
9537     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9538     struct GadgetInfo *gi;
9539     int button_type;
9540     boolean checked;
9541     unsigned long event_mask;
9542     int gd_xoffset, gd_yoffset;
9543     int gd_x1, gd_x2, gd_y1, gd_y2;
9544     int id = i;
9545
9546     gd_xoffset = gamebutton_info[i].x;
9547     gd_yoffset = gamebutton_info[i].y;
9548     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9549     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9550
9551     if (id == GAME_CTRL_ID_STOP ||
9552         id == GAME_CTRL_ID_PAUSE ||
9553         id == GAME_CTRL_ID_PLAY)
9554     {
9555       button_type = GD_TYPE_NORMAL_BUTTON;
9556       checked = FALSE;
9557       event_mask = GD_EVENT_RELEASED;
9558       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9559       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9560     }
9561     else
9562     {
9563       button_type = GD_TYPE_CHECK_BUTTON;
9564       checked =
9565         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9566          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9567          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9568       event_mask = GD_EVENT_PRESSED;
9569       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
9570       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9571     }
9572
9573     gi = CreateGadget(GDI_CUSTOM_ID, id,
9574                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
9575                       GDI_X, DX + gd_xoffset,
9576                       GDI_Y, DY + gd_yoffset,
9577                       GDI_WIDTH, GAME_BUTTON_XSIZE,
9578                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
9579                       GDI_TYPE, button_type,
9580                       GDI_STATE, GD_BUTTON_UNPRESSED,
9581                       GDI_CHECKED, checked,
9582                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9583                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9584                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9585                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9586                       GDI_EVENT_MASK, event_mask,
9587                       GDI_CALLBACK_ACTION, HandleGameButtons,
9588                       GDI_END);
9589
9590     if (gi == NULL)
9591       Error(ERR_EXIT, "cannot create gadget");
9592
9593     game_gadget[id] = gi;
9594   }
9595 }
9596
9597 void FreeGameButtons()
9598 {
9599   int i;
9600
9601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9602     FreeGadget(game_gadget[i]);
9603 }
9604
9605 static void MapGameButtons()
9606 {
9607   int i;
9608
9609   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9610     MapGadget(game_gadget[i]);
9611 }
9612
9613 void UnmapGameButtons()
9614 {
9615   int i;
9616
9617   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9618     UnmapGadget(game_gadget[i]);
9619 }
9620
9621 static void HandleGameButtons(struct GadgetInfo *gi)
9622 {
9623   int id = gi->custom_id;
9624
9625   if (game_status != GAME_MODE_PLAYING)
9626     return;
9627
9628   switch (id)
9629   {
9630     case GAME_CTRL_ID_STOP:
9631       RequestQuitGame(TRUE);
9632       break;
9633
9634     case GAME_CTRL_ID_PAUSE:
9635       if (options.network)
9636       {
9637 #if defined(PLATFORM_UNIX)
9638         if (tape.pausing)
9639           SendToServer_ContinuePlaying();
9640         else
9641           SendToServer_PausePlaying();
9642 #endif
9643       }
9644       else
9645         TapeTogglePause(TAPE_TOGGLE_MANUAL);
9646       break;
9647
9648     case GAME_CTRL_ID_PLAY:
9649       if (tape.pausing)
9650       {
9651 #if defined(PLATFORM_UNIX)
9652         if (options.network)
9653           SendToServer_ContinuePlaying();
9654         else
9655 #endif
9656         {
9657           tape.pausing = FALSE;
9658           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9659         }
9660       }
9661       break;
9662
9663     case SOUND_CTRL_ID_MUSIC:
9664       if (setup.sound_music)
9665       { 
9666         setup.sound_music = FALSE;
9667         FadeMusic();
9668       }
9669       else if (audio.music_available)
9670       { 
9671         setup.sound = setup.sound_music = TRUE;
9672
9673         SetAudioMode(setup.sound);
9674
9675         PlayLevelMusic();
9676       }
9677       break;
9678
9679     case SOUND_CTRL_ID_LOOPS:
9680       if (setup.sound_loops)
9681         setup.sound_loops = FALSE;
9682       else if (audio.loops_available)
9683       {
9684         setup.sound = setup.sound_loops = TRUE;
9685         SetAudioMode(setup.sound);
9686       }
9687       break;
9688
9689     case SOUND_CTRL_ID_SIMPLE:
9690       if (setup.sound_simple)
9691         setup.sound_simple = FALSE;
9692       else if (audio.sound_available)
9693       {
9694         setup.sound = setup.sound_simple = TRUE;
9695         SetAudioMode(setup.sound);
9696       }
9697       break;
9698
9699     default:
9700       break;
9701   }
9702 }