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