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