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