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