rnd-20031210-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         Feld[x][y] = EL_EMPTY;
4443         DrawLevelField(x, y);
4444
4445         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4446         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4447           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4448
4449         local_player->friends_still_needed--;
4450         if (!local_player->friends_still_needed &&
4451             !local_player->GameOver && AllPlayersGone)
4452           local_player->LevelSolved = local_player->GameOver = TRUE;
4453
4454         return;
4455       }
4456       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4457       {
4458         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4459           DrawLevelField(newx, newy);
4460         else
4461           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4462       }
4463       else if (!IS_FREE(newx, newy))
4464       {
4465         GfxAction[x][y] = ACTION_WAITING;
4466
4467         if (IS_PLAYER(x, y))
4468           DrawPlayerField(x, y);
4469         else
4470           DrawLevelField(x, y);
4471         return;
4472       }
4473     }
4474     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4475     {
4476       if (IS_FOOD_PIG(Feld[newx][newy]))
4477       {
4478         if (IS_MOVING(newx, newy))
4479           RemoveMovingField(newx, newy);
4480         else
4481         {
4482           Feld[newx][newy] = EL_EMPTY;
4483           DrawLevelField(newx, newy);
4484         }
4485
4486         PlayLevelSound(x, y, SND_PIG_DIGGING);
4487       }
4488       else if (!IS_FREE(newx, newy))
4489       {
4490         if (IS_PLAYER(x, y))
4491           DrawPlayerField(x, y);
4492         else
4493           DrawLevelField(x, y);
4494         return;
4495       }
4496     }
4497     else if ((move_pattern & MV_MAZE_RUNNER_STYLE ||
4498               element == EL_MAZE_RUNNER) && IN_LEV_FIELD(newx, newy))
4499     {
4500       if (IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4501       {
4502         if (IS_MOVING(newx, newy))
4503           RemoveMovingField(newx, newy);
4504         else
4505         {
4506           Feld[newx][newy] = EL_EMPTY;
4507           DrawLevelField(newx, newy);
4508         }
4509
4510         PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4511       }
4512       else if (!IS_FREE(newx, newy))
4513       {
4514 #if 0
4515         if (IS_PLAYER(x, y))
4516           DrawPlayerField(x, y);
4517         else
4518           DrawLevelField(x, y);
4519 #endif
4520         return;
4521       }
4522
4523       RunnerVisit[x][y] = FrameCounter;
4524       PlayerVisit[x][y] /= 8;           /* expire player visit path */
4525     }
4526     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4527     {
4528       if (!IS_FREE(newx, newy))
4529       {
4530         if (IS_PLAYER(x, y))
4531           DrawPlayerField(x, y);
4532         else
4533           DrawLevelField(x, y);
4534
4535         return;
4536       }
4537       else
4538       {
4539         boolean wanna_flame = !RND(10);
4540         int dx = newx - x, dy = newy - y;
4541         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4542         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4543         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4544                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4545         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4546                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4547
4548         if ((wanna_flame ||
4549              IS_CLASSIC_ENEMY(element1) ||
4550              IS_CLASSIC_ENEMY(element2)) &&
4551             element1 != EL_DRAGON && element2 != EL_DRAGON &&
4552             element1 != EL_FLAMES && element2 != EL_FLAMES)
4553         {
4554 #if 1
4555           ResetGfxAnimation(x, y);
4556           GfxAction[x][y] = ACTION_ATTACKING;
4557 #endif
4558
4559           if (IS_PLAYER(x, y))
4560             DrawPlayerField(x, y);
4561           else
4562             DrawLevelField(x, y);
4563
4564           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
4565
4566           MovDelay[x][y] = 50;
4567
4568           Feld[newx][newy] = EL_FLAMES;
4569           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4570             Feld[newx1][newy1] = EL_FLAMES;
4571           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4572             Feld[newx2][newy2] = EL_FLAMES;
4573
4574           return;
4575         }
4576       }
4577     }
4578     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4579              Feld[newx][newy] == EL_DIAMOND)
4580     {
4581       if (IS_MOVING(newx, newy))
4582         RemoveMovingField(newx, newy);
4583       else
4584       {
4585         Feld[newx][newy] = EL_EMPTY;
4586         DrawLevelField(newx, newy);
4587       }
4588
4589       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
4590     }
4591     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4592              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4593     {
4594       if (AmoebaNr[newx][newy])
4595       {
4596         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4597         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4598             Feld[newx][newy] == EL_BD_AMOEBA)
4599           AmoebaCnt[AmoebaNr[newx][newy]]--;
4600       }
4601
4602       if (IS_MOVING(newx, newy))
4603         RemoveMovingField(newx, newy);
4604       else
4605       {
4606         Feld[newx][newy] = EL_EMPTY;
4607         DrawLevelField(newx, newy);
4608       }
4609
4610       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
4611     }
4612     else if ((element == EL_PACMAN || element == EL_MOLE)
4613              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4614     {
4615       if (AmoebaNr[newx][newy])
4616       {
4617         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4618         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4619             Feld[newx][newy] == EL_BD_AMOEBA)
4620           AmoebaCnt[AmoebaNr[newx][newy]]--;
4621       }
4622
4623       if (element == EL_MOLE)
4624       {
4625         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4626         PlayLevelSound(x, y, SND_MOLE_DIGGING);
4627
4628         ResetGfxAnimation(x, y);
4629         GfxAction[x][y] = ACTION_DIGGING;
4630         DrawLevelField(x, y);
4631
4632         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
4633         return;                         /* wait for shrinking amoeba */
4634       }
4635       else      /* element == EL_PACMAN */
4636       {
4637         Feld[newx][newy] = EL_EMPTY;
4638         DrawLevelField(newx, newy);
4639         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
4640       }
4641     }
4642     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4643              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4644               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4645     {
4646       /* wait for shrinking amoeba to completely disappear */
4647       return;
4648     }
4649     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4650     {
4651       /* object was running against a wall */
4652
4653       TurnRound(x, y);
4654
4655 #if 1
4656       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
4657         DrawLevelElementAnimation(x, y, element);
4658 #else
4659       if (element == EL_BUG ||
4660           element == EL_SPACESHIP ||
4661           element == EL_SP_SNIKSNAK)
4662         DrawLevelField(x, y);
4663       else if (element == EL_MOLE)
4664         DrawLevelField(x, y);
4665       else if (element == EL_BD_BUTTERFLY ||
4666                element == EL_BD_FIREFLY)
4667         DrawLevelElementAnimationIfNeeded(x, y, element);
4668       else if (element == EL_SATELLITE)
4669         DrawLevelElementAnimationIfNeeded(x, y, element);
4670       else if (element == EL_SP_ELECTRON)
4671         DrawLevelElementAnimationIfNeeded(x, y, element);
4672 #endif
4673
4674       if (DONT_TOUCH(element))
4675         TestIfBadThingTouchesHero(x, y);
4676
4677 #if 0
4678       PlayLevelSoundAction(x, y, ACTION_WAITING);
4679 #endif
4680
4681       return;
4682     }
4683
4684     InitMovingField(x, y, MovDir[x][y]);
4685
4686     PlayLevelSoundAction(x, y, ACTION_MOVING);
4687   }
4688
4689   if (MovDir[x][y])
4690     ContinueMoving(x, y);
4691 }
4692
4693 void ContinueMoving(int x, int y)
4694 {
4695   int element = Feld[x][y];
4696   int direction = MovDir[x][y];
4697   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4698   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4699   int newx = x + dx, newy = y + dy;
4700   int nextx = newx + dx, nexty = newy + dy;
4701   boolean pushed = Pushed[x][y];
4702
4703   MovPos[x][y] += getElementMoveStepsize(x, y);
4704
4705   if (pushed)           /* special case: moving object pushed by player */
4706     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4707
4708   if (ABS(MovPos[x][y]) < TILEX)
4709   {
4710     DrawLevelField(x, y);
4711
4712     return;     /* element is still moving */
4713   }
4714
4715   /* element reached destination field */
4716
4717   Feld[x][y] = EL_EMPTY;
4718   Feld[newx][newy] = element;
4719   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
4720
4721   if (element == EL_MOLE)
4722   {
4723     Feld[x][y] = EL_SAND;
4724
4725     DrawLevelFieldCrumbledSandNeighbours(x, y);
4726   }
4727   else if (element == EL_QUICKSAND_FILLING)
4728   {
4729     element = Feld[newx][newy] = get_next_element(element);
4730     Store[newx][newy] = Store[x][y];
4731   }
4732   else if (element == EL_QUICKSAND_EMPTYING)
4733   {
4734     Feld[x][y] = get_next_element(element);
4735     element = Feld[newx][newy] = Store[x][y];
4736   }
4737   else if (element == EL_MAGIC_WALL_FILLING)
4738   {
4739     element = Feld[newx][newy] = get_next_element(element);
4740     if (!game.magic_wall_active)
4741       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4742     Store[newx][newy] = Store[x][y];
4743   }
4744   else if (element == EL_MAGIC_WALL_EMPTYING)
4745   {
4746     Feld[x][y] = get_next_element(element);
4747     if (!game.magic_wall_active)
4748       Feld[x][y] = EL_MAGIC_WALL_DEAD;
4749     element = Feld[newx][newy] = Store[x][y];
4750   }
4751   else if (element == EL_BD_MAGIC_WALL_FILLING)
4752   {
4753     element = Feld[newx][newy] = get_next_element(element);
4754     if (!game.magic_wall_active)
4755       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4756     Store[newx][newy] = Store[x][y];
4757   }
4758   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4759   {
4760     Feld[x][y] = get_next_element(element);
4761     if (!game.magic_wall_active)
4762       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4763     element = Feld[newx][newy] = Store[x][y];
4764   }
4765   else if (element == EL_AMOEBA_DROPPING)
4766   {
4767     Feld[x][y] = get_next_element(element);
4768     element = Feld[newx][newy] = Store[x][y];
4769   }
4770   else if (element == EL_SOKOBAN_OBJECT)
4771   {
4772     if (Back[x][y])
4773       Feld[x][y] = Back[x][y];
4774
4775     if (Back[newx][newy])
4776       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4777
4778     Back[x][y] = Back[newx][newy] = 0;
4779   }
4780   else if (Store[x][y] == EL_ACID)
4781   {
4782     element = Feld[newx][newy] = EL_ACID;
4783   }
4784
4785   Store[x][y] = 0;
4786   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4787   MovDelay[newx][newy] = 0;
4788
4789   /* copy element change control values to new field */
4790   ChangeDelay[newx][newy] = ChangeDelay[x][y];
4791   ChangePage[newx][newy] = ChangePage[x][y];
4792   Changed[newx][newy] = Changed[x][y];
4793   ChangeEvent[newx][newy] = ChangeEvent[x][y];
4794
4795   ChangeDelay[x][y] = 0;
4796   ChangePage[x][y] = -1;
4797   Changed[x][y] = CE_BITMASK_DEFAULT;
4798   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4799
4800   /* copy animation control values to new field */
4801   GfxFrame[newx][newy]  = GfxFrame[x][y];
4802   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
4803   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
4804   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
4805
4806   Pushed[x][y] = Pushed[newx][newy] = FALSE;
4807
4808   ResetGfxAnimation(x, y);      /* reset animation values for old field */
4809
4810 #if 0
4811   /* 2.1.1 (does not work correctly for spring) */
4812   if (!CAN_MOVE(element))
4813     MovDir[newx][newy] = 0;
4814 #else
4815
4816 #if 0
4817   /* (does not work for falling objects that slide horizontally) */
4818   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4819     MovDir[newx][newy] = 0;
4820 #else
4821   /*
4822   if (!CAN_MOVE(element) ||
4823       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4824     MovDir[newx][newy] = 0;
4825   */
4826
4827   if (!CAN_MOVE(element) ||
4828       (CAN_FALL(element) && direction == MV_DOWN))
4829     GfxDir[x][y] = MovDir[newx][newy] = 0;
4830
4831 #endif
4832 #endif
4833
4834   DrawLevelField(x, y);
4835   DrawLevelField(newx, newy);
4836
4837   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
4838
4839   /* prevent pushed element from moving on in pushed direction */
4840   if (pushed && CAN_MOVE(element) &&
4841       element_info[element].move_pattern & MV_ANY_DIRECTION &&
4842       !(element_info[element].move_pattern & direction))
4843     TurnRound(newx, newy);
4844
4845   if (!pushed)  /* special case: moving object pushed by player */
4846   {
4847     WasJustMoving[newx][newy] = 3;
4848
4849     if (CAN_FALL(element) && direction == MV_DOWN)
4850       WasJustFalling[newx][newy] = 3;
4851   }
4852
4853   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
4854   {
4855     TestIfBadThingTouchesHero(newx, newy);
4856     TestIfBadThingTouchesFriend(newx, newy);
4857
4858     if (!IS_CUSTOM_ELEMENT(element))
4859       TestIfBadThingTouchesOtherBadThing(newx, newy);
4860   }
4861   else if (element == EL_PENGUIN)
4862     TestIfFriendTouchesBadThing(newx, newy);
4863
4864   if (CAN_FALL(element) && direction == MV_DOWN &&
4865       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4866     Impact(x, newy);
4867
4868 #if 1
4869   TestIfElementTouchesCustomElement(x, y);              /* for empty space */
4870 #endif
4871
4872 #if 0
4873   if (ChangePage[newx][newy] != -1)                     /* delayed change */
4874     ChangeElement(newx, newy, ChangePage[newx][newy]);
4875 #endif
4876
4877   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4878   {
4879     /* !!! fix side (direction) orientation here and elsewhere !!! */
4880     CheckElementSideChange(newx, newy, Feld[newx][newy],
4881                            direction, CE_COLLISION_ACTIVE, -1);
4882
4883 #if 0
4884     if (IN_LEV_FIELD(nextx, nexty))
4885     {
4886       static int opposite_directions[] =
4887       {
4888         MV_RIGHT,
4889         MV_LEFT,
4890         MV_DOWN,
4891         MV_UP
4892       };
4893       int move_dir_bit = MV_DIR_BIT(direction);
4894       int opposite_direction = opposite_directions[move_dir_bit];
4895       int hitting_side = direction;
4896       int touched_side = opposite_direction;
4897       int hitting_element = Feld[newx][newy];
4898       int touched_element = MovingOrBlocked2Element(nextx, nexty);
4899       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
4900                             MovDir[nextx][nexty] != direction ||
4901                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
4902
4903       if (object_hit)
4904       {
4905         int i;
4906
4907         CheckElementSideChange(nextx, nexty, Feld[nextx][nexty],
4908                                opposite_direction, CE_COLLISION_PASSIVE, -1);
4909
4910         if (IS_CUSTOM_ELEMENT(hitting_element) &&
4911             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_COLL_ACTIVE))
4912         {
4913           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
4914           {
4915             struct ElementChangeInfo *change =
4916               &element_info[hitting_element].change_page[i];
4917
4918             if (change->can_change &&
4919                 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_ACTIVE) &&
4920                 change->sides & touched_side &&
4921                 change->trigger_element == touched_element)
4922             {
4923               CheckElementSideChange(newx, newy, hitting_element,
4924                                      CH_SIDE_ANY, CE_OTHER_IS_COLL_ACTIVE, i);
4925               break;
4926             }
4927           }
4928         }
4929
4930         if (IS_CUSTOM_ELEMENT(touched_element) &&
4931             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_IS_COLL_PASSIVE))
4932         {
4933           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
4934           {
4935             struct ElementChangeInfo *change =
4936               &element_info[touched_element].change_page[i];
4937
4938             if (change->can_change &&
4939                 change->events & CH_EVENT_BIT(CE_OTHER_IS_COLL_PASSIVE) &&
4940                 change->sides & hitting_side &&
4941                 change->trigger_element == hitting_element)
4942             {
4943               CheckElementSideChange(nextx, nexty, touched_element,
4944                                      CH_SIDE_ANY, CE_OTHER_IS_COLL_PASSIVE, i);
4945               break;
4946             }
4947           }
4948         }
4949       }
4950     }
4951 #endif
4952   }
4953
4954   TestIfPlayerTouchesCustomElement(newx, newy);
4955   TestIfElementTouchesCustomElement(newx, newy);
4956 }
4957
4958 int AmoebeNachbarNr(int ax, int ay)
4959 {
4960   int i;
4961   int element = Feld[ax][ay];
4962   int group_nr = 0;
4963   static int xy[4][2] =
4964   {
4965     { 0, -1 },
4966     { -1, 0 },
4967     { +1, 0 },
4968     { 0, +1 }
4969   };
4970
4971   for (i = 0; i < 4; i++)
4972   {
4973     int x = ax + xy[i][0];
4974     int y = ay + xy[i][1];
4975
4976     if (!IN_LEV_FIELD(x, y))
4977       continue;
4978
4979     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4980       group_nr = AmoebaNr[x][y];
4981   }
4982
4983   return group_nr;
4984 }
4985
4986 void AmoebenVereinigen(int ax, int ay)
4987 {
4988   int i, x, y, xx, yy;
4989   int new_group_nr = AmoebaNr[ax][ay];
4990   static int xy[4][2] =
4991   {
4992     { 0, -1 },
4993     { -1, 0 },
4994     { +1, 0 },
4995     { 0, +1 }
4996   };
4997
4998   if (new_group_nr == 0)
4999     return;
5000
5001   for (i = 0; i < 4; i++)
5002   {
5003     x = ax + xy[i][0];
5004     y = ay + xy[i][1];
5005
5006     if (!IN_LEV_FIELD(x, y))
5007       continue;
5008
5009     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5010          Feld[x][y] == EL_BD_AMOEBA ||
5011          Feld[x][y] == EL_AMOEBA_DEAD) &&
5012         AmoebaNr[x][y] != new_group_nr)
5013     {
5014       int old_group_nr = AmoebaNr[x][y];
5015
5016       if (old_group_nr == 0)
5017         return;
5018
5019       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5020       AmoebaCnt[old_group_nr] = 0;
5021       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5022       AmoebaCnt2[old_group_nr] = 0;
5023
5024       for (yy = 0; yy < lev_fieldy; yy++)
5025       {
5026         for (xx = 0; xx < lev_fieldx; xx++)
5027         {
5028           if (AmoebaNr[xx][yy] == old_group_nr)
5029             AmoebaNr[xx][yy] = new_group_nr;
5030         }
5031       }
5032     }
5033   }
5034 }
5035
5036 void AmoebeUmwandeln(int ax, int ay)
5037 {
5038   int i, x, y;
5039
5040   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5041   {
5042     int group_nr = AmoebaNr[ax][ay];
5043
5044 #ifdef DEBUG
5045     if (group_nr == 0)
5046     {
5047       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5048       printf("AmoebeUmwandeln(): This should never happen!\n");
5049       return;
5050     }
5051 #endif
5052
5053     for (y = 0; y < lev_fieldy; y++)
5054     {
5055       for (x = 0; x < lev_fieldx; x++)
5056       {
5057         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5058         {
5059           AmoebaNr[x][y] = 0;
5060           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5061         }
5062       }
5063     }
5064     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5065                             SND_AMOEBA_TURNING_TO_GEM :
5066                             SND_AMOEBA_TURNING_TO_ROCK));
5067     Bang(ax, ay);
5068   }
5069   else
5070   {
5071     static int xy[4][2] =
5072     {
5073       { 0, -1 },
5074       { -1, 0 },
5075       { +1, 0 },
5076       { 0, +1 }
5077     };
5078
5079     for (i = 0; i < 4; i++)
5080     {
5081       x = ax + xy[i][0];
5082       y = ay + xy[i][1];
5083
5084       if (!IN_LEV_FIELD(x, y))
5085         continue;
5086
5087       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5088       {
5089         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5090                               SND_AMOEBA_TURNING_TO_GEM :
5091                               SND_AMOEBA_TURNING_TO_ROCK));
5092         Bang(x, y);
5093       }
5094     }
5095   }
5096 }
5097
5098 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5099 {
5100   int x, y;
5101   int group_nr = AmoebaNr[ax][ay];
5102   boolean done = FALSE;
5103
5104 #ifdef DEBUG
5105   if (group_nr == 0)
5106   {
5107     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5108     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5109     return;
5110   }
5111 #endif
5112
5113   for (y = 0; y < lev_fieldy; y++)
5114   {
5115     for (x = 0; x < lev_fieldx; x++)
5116     {
5117       if (AmoebaNr[x][y] == group_nr &&
5118           (Feld[x][y] == EL_AMOEBA_DEAD ||
5119            Feld[x][y] == EL_BD_AMOEBA ||
5120            Feld[x][y] == EL_AMOEBA_GROWING))
5121       {
5122         AmoebaNr[x][y] = 0;
5123         Feld[x][y] = new_element;
5124         InitField(x, y, FALSE);
5125         DrawLevelField(x, y);
5126         done = TRUE;
5127       }
5128     }
5129   }
5130
5131   if (done)
5132     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5133                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5134                             SND_BD_AMOEBA_TURNING_TO_GEM));
5135 }
5136
5137 void AmoebeWaechst(int x, int y)
5138 {
5139   static unsigned long sound_delay = 0;
5140   static unsigned long sound_delay_value = 0;
5141
5142   if (!MovDelay[x][y])          /* start new growing cycle */
5143   {
5144     MovDelay[x][y] = 7;
5145
5146     if (DelayReached(&sound_delay, sound_delay_value))
5147     {
5148 #if 1
5149       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5150 #else
5151       if (Store[x][y] == EL_BD_AMOEBA)
5152         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5153       else
5154         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5155 #endif
5156       sound_delay_value = 30;
5157     }
5158   }
5159
5160   if (MovDelay[x][y])           /* wait some time before growing bigger */
5161   {
5162     MovDelay[x][y]--;
5163     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5164     {
5165       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5166                                            6 - MovDelay[x][y]);
5167
5168       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5169     }
5170
5171     if (!MovDelay[x][y])
5172     {
5173       Feld[x][y] = Store[x][y];
5174       Store[x][y] = 0;
5175       DrawLevelField(x, y);
5176     }
5177   }
5178 }
5179
5180 void AmoebaDisappearing(int x, int y)
5181 {
5182   static unsigned long sound_delay = 0;
5183   static unsigned long sound_delay_value = 0;
5184
5185   if (!MovDelay[x][y])          /* start new shrinking cycle */
5186   {
5187     MovDelay[x][y] = 7;
5188
5189     if (DelayReached(&sound_delay, sound_delay_value))
5190       sound_delay_value = 30;
5191   }
5192
5193   if (MovDelay[x][y])           /* wait some time before shrinking */
5194   {
5195     MovDelay[x][y]--;
5196     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5197     {
5198       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5199                                            6 - MovDelay[x][y]);
5200
5201       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5202     }
5203
5204     if (!MovDelay[x][y])
5205     {
5206       Feld[x][y] = EL_EMPTY;
5207       DrawLevelField(x, y);
5208
5209       /* don't let mole enter this field in this cycle;
5210          (give priority to objects falling to this field from above) */
5211       Stop[x][y] = TRUE;
5212     }
5213   }
5214 }
5215
5216 void AmoebeAbleger(int ax, int ay)
5217 {
5218   int i;
5219   int element = Feld[ax][ay];
5220   int graphic = el2img(element);
5221   int newax = ax, neway = ay;
5222   static int xy[4][2] =
5223   {
5224     { 0, -1 },
5225     { -1, 0 },
5226     { +1, 0 },
5227     { 0, +1 }
5228   };
5229
5230   if (!level.amoeba_speed)
5231   {
5232     Feld[ax][ay] = EL_AMOEBA_DEAD;
5233     DrawLevelField(ax, ay);
5234     return;
5235   }
5236
5237   if (IS_ANIMATED(graphic))
5238     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5239
5240   if (!MovDelay[ax][ay])        /* start making new amoeba field */
5241     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5242
5243   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
5244   {
5245     MovDelay[ax][ay]--;
5246     if (MovDelay[ax][ay])
5247       return;
5248   }
5249
5250   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5251   {
5252     int start = RND(4);
5253     int x = ax + xy[start][0];
5254     int y = ay + xy[start][1];
5255
5256     if (!IN_LEV_FIELD(x, y))
5257       return;
5258
5259     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5260     if (IS_FREE(x, y) ||
5261         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5262     {
5263       newax = x;
5264       neway = y;
5265     }
5266
5267     if (newax == ax && neway == ay)
5268       return;
5269   }
5270   else                          /* normal or "filled" (BD style) amoeba */
5271   {
5272     int start = RND(4);
5273     boolean waiting_for_player = FALSE;
5274
5275     for (i = 0; i < 4; i++)
5276     {
5277       int j = (start + i) % 4;
5278       int x = ax + xy[j][0];
5279       int y = ay + xy[j][1];
5280
5281       if (!IN_LEV_FIELD(x, y))
5282         continue;
5283
5284       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5285       if (IS_FREE(x, y) ||
5286           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5287       {
5288         newax = x;
5289         neway = y;
5290         break;
5291       }
5292       else if (IS_PLAYER(x, y))
5293         waiting_for_player = TRUE;
5294     }
5295
5296     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5297     {
5298       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5299       {
5300         Feld[ax][ay] = EL_AMOEBA_DEAD;
5301         DrawLevelField(ax, ay);
5302         AmoebaCnt[AmoebaNr[ax][ay]]--;
5303
5304         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5305         {
5306           if (element == EL_AMOEBA_FULL)
5307             AmoebeUmwandeln(ax, ay);
5308           else if (element == EL_BD_AMOEBA)
5309             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5310         }
5311       }
5312       return;
5313     }
5314     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5315     {
5316       /* amoeba gets larger by growing in some direction */
5317
5318       int new_group_nr = AmoebaNr[ax][ay];
5319
5320 #ifdef DEBUG
5321   if (new_group_nr == 0)
5322   {
5323     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5324     printf("AmoebeAbleger(): This should never happen!\n");
5325     return;
5326   }
5327 #endif
5328
5329       AmoebaNr[newax][neway] = new_group_nr;
5330       AmoebaCnt[new_group_nr]++;
5331       AmoebaCnt2[new_group_nr]++;
5332
5333       /* if amoeba touches other amoeba(s) after growing, unify them */
5334       AmoebenVereinigen(newax, neway);
5335
5336       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5337       {
5338         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5339         return;
5340       }
5341     }
5342   }
5343
5344   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5345       (neway == lev_fieldy - 1 && newax != ax))
5346   {
5347     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5348     Store[newax][neway] = element;
5349   }
5350   else if (neway == ay)
5351   {
5352     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5353 #if 1
5354     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5355 #else
5356     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5357 #endif
5358   }
5359   else
5360   {
5361     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5362     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5363     Store[ax][ay] = EL_AMOEBA_DROP;
5364     ContinueMoving(ax, ay);
5365     return;
5366   }
5367
5368   DrawLevelField(newax, neway);
5369 }
5370
5371 void Life(int ax, int ay)
5372 {
5373   int x1, y1, x2, y2;
5374   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5375   int life_time = 40;
5376   int element = Feld[ax][ay];
5377   int graphic = el2img(element);
5378   boolean changed = FALSE;
5379
5380   if (IS_ANIMATED(graphic))
5381     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5382
5383   if (Stop[ax][ay])
5384     return;
5385
5386   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5387     MovDelay[ax][ay] = life_time;
5388
5389   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5390   {
5391     MovDelay[ax][ay]--;
5392     if (MovDelay[ax][ay])
5393       return;
5394   }
5395
5396   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5397   {
5398     int xx = ax+x1, yy = ay+y1;
5399     int nachbarn = 0;
5400
5401     if (!IN_LEV_FIELD(xx, yy))
5402       continue;
5403
5404     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5405     {
5406       int x = xx+x2, y = yy+y2;
5407
5408       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5409         continue;
5410
5411       if (((Feld[x][y] == element ||
5412             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5413            !Stop[x][y]) ||
5414           (IS_FREE(x, y) && Stop[x][y]))
5415         nachbarn++;
5416     }
5417
5418     if (xx == ax && yy == ay)           /* field in the middle */
5419     {
5420       if (nachbarn < life[0] || nachbarn > life[1])
5421       {
5422         Feld[xx][yy] = EL_EMPTY;
5423         if (!Stop[xx][yy])
5424           DrawLevelField(xx, yy);
5425         Stop[xx][yy] = TRUE;
5426         changed = TRUE;
5427       }
5428     }
5429     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5430     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5431     {                                   /* free border field */
5432       if (nachbarn >= life[2] && nachbarn <= life[3])
5433       {
5434         Feld[xx][yy] = element;
5435         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5436         if (!Stop[xx][yy])
5437           DrawLevelField(xx, yy);
5438         Stop[xx][yy] = TRUE;
5439         changed = TRUE;
5440       }
5441     }
5442   }
5443
5444   if (changed)
5445     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5446                    SND_GAME_OF_LIFE_GROWING);
5447 }
5448
5449 static void InitRobotWheel(int x, int y)
5450 {
5451   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5452 }
5453
5454 static void RunRobotWheel(int x, int y)
5455 {
5456   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5457 }
5458
5459 static void StopRobotWheel(int x, int y)
5460 {
5461   if (ZX == x && ZY == y)
5462     ZX = ZY = -1;
5463 }
5464
5465 static void InitTimegateWheel(int x, int y)
5466 {
5467   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5468 }
5469
5470 static void RunTimegateWheel(int x, int y)
5471 {
5472   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5473 }
5474
5475 void CheckExit(int x, int y)
5476 {
5477   if (local_player->gems_still_needed > 0 ||
5478       local_player->sokobanfields_still_needed > 0 ||
5479       local_player->lights_still_needed > 0)
5480   {
5481     int element = Feld[x][y];
5482     int graphic = el2img(element);
5483
5484     if (IS_ANIMATED(graphic))
5485       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5486
5487     return;
5488   }
5489
5490   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5491     return;
5492
5493   Feld[x][y] = EL_EXIT_OPENING;
5494
5495   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5496 }
5497
5498 void CheckExitSP(int x, int y)
5499 {
5500   if (local_player->gems_still_needed > 0)
5501   {
5502     int element = Feld[x][y];
5503     int graphic = el2img(element);
5504
5505     if (IS_ANIMATED(graphic))
5506       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5507
5508     return;
5509   }
5510
5511   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5512     return;
5513
5514   Feld[x][y] = EL_SP_EXIT_OPENING;
5515
5516   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5517 }
5518
5519 static void CloseAllOpenTimegates()
5520 {
5521   int x, y;
5522
5523   for (y = 0; y < lev_fieldy; y++)
5524   {
5525     for (x = 0; x < lev_fieldx; x++)
5526     {
5527       int element = Feld[x][y];
5528
5529       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5530       {
5531         Feld[x][y] = EL_TIMEGATE_CLOSING;
5532 #if 1
5533         PlayLevelSoundAction(x, y, ACTION_CLOSING);
5534 #else
5535         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5536 #endif
5537       }
5538     }
5539   }
5540 }
5541
5542 void EdelsteinFunkeln(int x, int y)
5543 {
5544   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5545     return;
5546
5547   if (Feld[x][y] == EL_BD_DIAMOND)
5548     return;
5549
5550   if (MovDelay[x][y] == 0)      /* next animation frame */
5551     MovDelay[x][y] = 11 * !SimpleRND(500);
5552
5553   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
5554   {
5555     MovDelay[x][y]--;
5556
5557     if (setup.direct_draw && MovDelay[x][y])
5558       SetDrawtoField(DRAW_BUFFERED);
5559
5560     DrawLevelElementAnimation(x, y, Feld[x][y]);
5561
5562     if (MovDelay[x][y] != 0)
5563     {
5564       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5565                                            10 - MovDelay[x][y]);
5566
5567       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5568
5569       if (setup.direct_draw)
5570       {
5571         int dest_x, dest_y;
5572
5573         dest_x = FX + SCREENX(x) * TILEX;
5574         dest_y = FY + SCREENY(y) * TILEY;
5575
5576         BlitBitmap(drawto_field, window,
5577                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5578         SetDrawtoField(DRAW_DIRECT);
5579       }
5580     }
5581   }
5582 }
5583
5584 void MauerWaechst(int x, int y)
5585 {
5586   int delay = 6;
5587
5588   if (!MovDelay[x][y])          /* next animation frame */
5589     MovDelay[x][y] = 3 * delay;
5590
5591   if (MovDelay[x][y])           /* wait some time before next frame */
5592   {
5593     MovDelay[x][y]--;
5594
5595     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5596     {
5597       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5598       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5599
5600       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5601     }
5602
5603     if (!MovDelay[x][y])
5604     {
5605       if (MovDir[x][y] == MV_LEFT)
5606       {
5607         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5608           DrawLevelField(x - 1, y);
5609       }
5610       else if (MovDir[x][y] == MV_RIGHT)
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_UP)
5616       {
5617         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5618           DrawLevelField(x, y - 1);
5619       }
5620       else
5621       {
5622         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5623           DrawLevelField(x, y + 1);
5624       }
5625
5626       Feld[x][y] = Store[x][y];
5627       Store[x][y] = 0;
5628       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5629       DrawLevelField(x, y);
5630     }
5631   }
5632 }
5633
5634 void MauerAbleger(int ax, int ay)
5635 {
5636   int element = Feld[ax][ay];
5637   int graphic = el2img(element);
5638   boolean oben_frei = FALSE, unten_frei = FALSE;
5639   boolean links_frei = FALSE, rechts_frei = FALSE;
5640   boolean oben_massiv = FALSE, unten_massiv = FALSE;
5641   boolean links_massiv = FALSE, rechts_massiv = FALSE;
5642   boolean new_wall = FALSE;
5643
5644   if (IS_ANIMATED(graphic))
5645     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5646
5647   if (!MovDelay[ax][ay])        /* start building new wall */
5648     MovDelay[ax][ay] = 6;
5649
5650   if (MovDelay[ax][ay])         /* wait some time before building new wall */
5651   {
5652     MovDelay[ax][ay]--;
5653     if (MovDelay[ax][ay])
5654       return;
5655   }
5656
5657   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5658     oben_frei = TRUE;
5659   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5660     unten_frei = TRUE;
5661   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5662     links_frei = TRUE;
5663   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5664     rechts_frei = TRUE;
5665
5666   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5667       element == EL_EXPANDABLE_WALL_ANY)
5668   {
5669     if (oben_frei)
5670     {
5671       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5672       Store[ax][ay-1] = element;
5673       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5674       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5675         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5676                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5677       new_wall = TRUE;
5678     }
5679     if (unten_frei)
5680     {
5681       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5682       Store[ax][ay+1] = element;
5683       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5684       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5685         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5686                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5687       new_wall = TRUE;
5688     }
5689   }
5690
5691   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5692       element == EL_EXPANDABLE_WALL_ANY ||
5693       element == EL_EXPANDABLE_WALL)
5694   {
5695     if (links_frei)
5696     {
5697       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5698       Store[ax-1][ay] = element;
5699       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5700       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5701         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5702                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5703       new_wall = TRUE;
5704     }
5705
5706     if (rechts_frei)
5707     {
5708       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5709       Store[ax+1][ay] = element;
5710       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5711       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5712         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5713                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5714       new_wall = TRUE;
5715     }
5716   }
5717
5718   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5719     DrawLevelField(ax, ay);
5720
5721   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5722     oben_massiv = TRUE;
5723   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5724     unten_massiv = TRUE;
5725   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5726     links_massiv = TRUE;
5727   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5728     rechts_massiv = TRUE;
5729
5730   if (((oben_massiv && unten_massiv) ||
5731        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5732        element == EL_EXPANDABLE_WALL) &&
5733       ((links_massiv && rechts_massiv) ||
5734        element == EL_EXPANDABLE_WALL_VERTICAL))
5735     Feld[ax][ay] = EL_WALL;
5736
5737   if (new_wall)
5738 #if 1
5739     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
5740 #else
5741     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5742 #endif
5743 }
5744
5745 void CheckForDragon(int x, int y)
5746 {
5747   int i, j;
5748   boolean dragon_found = FALSE;
5749   static int xy[4][2] =
5750   {
5751     { 0, -1 },
5752     { -1, 0 },
5753     { +1, 0 },
5754     { 0, +1 }
5755   };
5756
5757   for (i = 0; i < 4; i++)
5758   {
5759     for (j = 0; j < 4; j++)
5760     {
5761       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5762
5763       if (IN_LEV_FIELD(xx, yy) &&
5764           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5765       {
5766         if (Feld[xx][yy] == EL_DRAGON)
5767           dragon_found = TRUE;
5768       }
5769       else
5770         break;
5771     }
5772   }
5773
5774   if (!dragon_found)
5775   {
5776     for (i = 0; i < 4; i++)
5777     {
5778       for (j = 0; j < 3; j++)
5779       {
5780         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5781   
5782         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5783         {
5784           Feld[xx][yy] = EL_EMPTY;
5785           DrawLevelField(xx, yy);
5786         }
5787         else
5788           break;
5789       }
5790     }
5791   }
5792 }
5793
5794 static void InitBuggyBase(int x, int y)
5795 {
5796   int element = Feld[x][y];
5797   int activating_delay = FRAMES_PER_SECOND / 4;
5798
5799   ChangeDelay[x][y] =
5800     (element == EL_SP_BUGGY_BASE ?
5801      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5802      element == EL_SP_BUGGY_BASE_ACTIVATING ?
5803      activating_delay :
5804      element == EL_SP_BUGGY_BASE_ACTIVE ?
5805      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5806 }
5807
5808 static void WarnBuggyBase(int x, int y)
5809 {
5810   int i;
5811   static int xy[4][2] =
5812   {
5813     { 0, -1 },
5814     { -1, 0 },
5815     { +1, 0 },
5816     { 0, +1 }
5817   };
5818
5819   for (i = 0; i < 4; i++)
5820   {
5821     int xx = x + xy[i][0], yy = y + xy[i][1];
5822
5823     if (IS_PLAYER(xx, yy))
5824     {
5825       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5826
5827       break;
5828     }
5829   }
5830 }
5831
5832 static void InitTrap(int x, int y)
5833 {
5834   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5835 }
5836
5837 static void ActivateTrap(int x, int y)
5838 {
5839   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
5840 }
5841
5842 static void ChangeActiveTrap(int x, int y)
5843 {
5844   int graphic = IMG_TRAP_ACTIVE;
5845
5846   /* if new animation frame was drawn, correct crumbled sand border */
5847   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5848     DrawLevelFieldCrumbledSand(x, y);
5849 }
5850
5851 static void ChangeElementNowExt(int x, int y, int target_element)
5852 {
5853   /* check if element under player changes from accessible to unaccessible
5854      (needed for special case of dropping element which then changes) */
5855   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5856       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5857   {
5858     Bang(x, y);
5859     return;
5860   }
5861
5862   RemoveField(x, y);
5863   Feld[x][y] = target_element;
5864
5865   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5866
5867   ResetGfxAnimation(x, y);
5868   ResetRandomAnimationValue(x, y);
5869
5870   InitField(x, y, FALSE);
5871   if (CAN_MOVE(Feld[x][y]))
5872     InitMovDir(x, y);
5873
5874   DrawLevelField(x, y);
5875
5876   if (GFX_CRUMBLED(Feld[x][y]))
5877     DrawLevelFieldCrumbledSandNeighbours(x, y);
5878
5879   TestIfBadThingTouchesHero(x, y);
5880   TestIfPlayerTouchesCustomElement(x, y);
5881   TestIfElementTouchesCustomElement(x, y);
5882
5883   if (ELEM_IS_PLAYER(target_element))
5884     RelocatePlayer(x, y, target_element);
5885 }
5886
5887 static boolean ChangeElementNow(int x, int y, int element, int page)
5888 {
5889   struct ElementChangeInfo *change = &element_info[element].change_page[page];
5890
5891   /* always use default change event to prevent running into a loop */
5892   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5893     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5894
5895   /* do not change already changed elements with same change event */
5896 #if 0
5897   if (Changed[x][y] & ChangeEvent[x][y])
5898     return FALSE;
5899 #else
5900   if (Changed[x][y])
5901     return FALSE;
5902 #endif
5903
5904   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5905
5906   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5907
5908   if (change->explode)
5909   {
5910     Bang(x, y);
5911
5912     return TRUE;
5913   }
5914
5915   if (change->use_content)
5916   {
5917     boolean complete_change = TRUE;
5918     boolean can_change[3][3];
5919     int xx, yy;
5920
5921     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5922     {
5923       boolean half_destructible;
5924       int ex = x + xx - 1;
5925       int ey = y + yy - 1;
5926       int e;
5927
5928       can_change[xx][yy] = TRUE;
5929
5930       if (ex == x && ey == y)   /* do not check changing element itself */
5931         continue;
5932
5933       if (change->content[xx][yy] == EL_EMPTY_SPACE)
5934       {
5935         can_change[xx][yy] = FALSE;     /* do not change empty borders */
5936
5937         continue;
5938       }
5939
5940       if (!IN_LEV_FIELD(ex, ey))
5941       {
5942         can_change[xx][yy] = FALSE;
5943         complete_change = FALSE;
5944
5945         continue;
5946       }
5947
5948       e = Feld[ex][ey];
5949
5950       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5951         e = MovingOrBlocked2Element(ex, ey);
5952
5953       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5954
5955       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
5956           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5957           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5958       {
5959         can_change[xx][yy] = FALSE;
5960         complete_change = FALSE;
5961       }
5962     }
5963
5964     if (!change->only_complete || complete_change)
5965     {
5966       boolean something_has_changed = FALSE;
5967
5968       if (change->only_complete && change->use_random_change &&
5969           RND(100) < change->random)
5970         return FALSE;
5971
5972       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
5973       {
5974         int ex = x + xx - 1;
5975         int ey = y + yy - 1;
5976
5977         if (can_change[xx][yy] && (!change->use_random_change ||
5978                                    RND(100) < change->random))
5979         {
5980           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5981             RemoveMovingField(ex, ey);
5982
5983           ChangeEvent[ex][ey] = ChangeEvent[x][y];
5984
5985           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5986
5987           something_has_changed = TRUE;
5988
5989           /* for symmetry reasons, freeze newly created border elements */
5990           if (ex != x || ey != y)
5991             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
5992         }
5993       }
5994
5995       if (something_has_changed)
5996         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
5997     }
5998   }
5999   else
6000   {
6001     ChangeElementNowExt(x, y, change->target_element);
6002
6003     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6004   }
6005
6006   return TRUE;
6007 }
6008
6009 static void ChangeElement(int x, int y, int page)
6010 {
6011   int element = MovingOrBlocked2Element(x, y);
6012   struct ElementInfo *ei = &element_info[element];
6013   struct ElementChangeInfo *change = &ei->change_page[page];
6014
6015 #if 0
6016 #ifdef DEBUG
6017   if (!CAN_CHANGE(element))
6018   {
6019     printf("\n\n");
6020     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6021            x, y, element, element_info[element].token_name);
6022     printf("ChangeElement(): This should never happen!\n");
6023     printf("\n\n");
6024   }
6025 #endif
6026 #endif
6027
6028   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6029   {
6030     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6031                          RND(change->delay_random * change->delay_frames)) + 1;
6032
6033     ResetGfxAnimation(x, y);
6034     ResetRandomAnimationValue(x, y);
6035
6036     if (change->pre_change_function)
6037       change->pre_change_function(x, y);
6038   }
6039
6040   ChangeDelay[x][y]--;
6041
6042   if (ChangeDelay[x][y] != 0)           /* continue element change */
6043   {
6044     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6045
6046     if (IS_ANIMATED(graphic))
6047       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6048
6049     if (change->change_function)
6050       change->change_function(x, y);
6051   }
6052   else                                  /* finish element change */
6053   {
6054     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6055     {
6056       page = ChangePage[x][y];
6057       ChangePage[x][y] = -1;
6058     }
6059
6060     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6061     {
6062       ChangeDelay[x][y] = 1;            /* try change after next move step */
6063       ChangePage[x][y] = page;          /* remember page to use for change */
6064
6065       return;
6066     }
6067
6068     if (ChangeElementNow(x, y, element, page))
6069     {
6070       if (change->post_change_function)
6071         change->post_change_function(x, y);
6072     }
6073   }
6074 }
6075
6076 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6077                                                int trigger_element,
6078                                                int trigger_side,
6079                                                int trigger_event)
6080 {
6081   int i, j, x, y;
6082
6083   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6084     return FALSE;
6085
6086   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6087   {
6088     int element = EL_CUSTOM_START + i;
6089
6090     boolean change_element = FALSE;
6091     int page = 0;
6092
6093     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6094       continue;
6095
6096     for (j = 0; j < element_info[element].num_change_pages; j++)
6097     {
6098       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6099
6100       if (change->can_change &&
6101 #if 1
6102           change->events & CH_EVENT_BIT(trigger_event) &&
6103 #endif
6104           change->sides & trigger_side &&
6105           change->trigger_element == trigger_element)
6106       {
6107 #if 0
6108         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6109           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6110                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6111 #endif
6112
6113         change_element = TRUE;
6114         page = j;
6115
6116         break;
6117       }
6118     }
6119
6120     if (!change_element)
6121       continue;
6122
6123     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6124     {
6125 #if 0
6126       if (x == lx && y == ly)   /* do not change trigger element itself */
6127         continue;
6128 #endif
6129
6130       if (Feld[x][y] == element)
6131       {
6132         ChangeDelay[x][y] = 1;
6133         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6134         ChangeElement(x, y, page);
6135       }
6136     }
6137   }
6138
6139   return TRUE;
6140 }
6141
6142 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6143                                            int trigger_event)
6144 {
6145   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6146                                          trigger_event);
6147 }
6148
6149 static boolean CheckElementSideChange(int x, int y, int element, int side,
6150                                       int trigger_event, int page)
6151 {
6152   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6153     return FALSE;
6154
6155   if (Feld[x][y] == EL_BLOCKED)
6156   {
6157     Blocked2Moving(x, y, &x, &y);
6158     element = Feld[x][y];
6159   }
6160
6161   if (page < 0)
6162     page = element_info[element].event_page_nr[trigger_event];
6163
6164   if (!(element_info[element].change_page[page].sides & side))
6165     return FALSE;
6166
6167   ChangeDelay[x][y] = 1;
6168   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6169   ChangeElement(x, y, page);
6170
6171   return TRUE;
6172 }
6173
6174 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6175 {
6176   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6177 }
6178
6179 static void PlayPlayerSound(struct PlayerInfo *player)
6180 {
6181   int jx = player->jx, jy = player->jy;
6182   int element = player->element_nr;
6183   int last_action = player->last_action_waiting;
6184   int action = player->action_waiting;
6185
6186   if (player->is_waiting)
6187   {
6188     if (action != last_action)
6189       PlayLevelSoundElementAction(jx, jy, element, action);
6190     else
6191       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6192   }
6193   else
6194   {
6195     if (action != last_action)
6196       StopSound(element_info[element].sound[last_action]);
6197
6198     if (last_action == ACTION_SLEEPING)
6199       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6200   }
6201 }
6202
6203 static void PlayAllPlayersSound()
6204 {
6205   int i;
6206
6207   for (i = 0; i < MAX_PLAYERS; i++)
6208     if (stored_player[i].active)
6209       PlayPlayerSound(&stored_player[i]);
6210 }
6211
6212 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6213 {
6214   boolean last_waiting = player->is_waiting;
6215   int move_dir = player->MovDir;
6216
6217   player->last_action_waiting = player->action_waiting;
6218
6219   if (is_waiting)
6220   {
6221     if (!last_waiting)          /* not waiting -> waiting */
6222     {
6223       player->is_waiting = TRUE;
6224
6225       player->frame_counter_bored =
6226         FrameCounter +
6227         game.player_boring_delay_fixed +
6228         SimpleRND(game.player_boring_delay_random);
6229       player->frame_counter_sleeping =
6230         FrameCounter +
6231         game.player_sleeping_delay_fixed +
6232         SimpleRND(game.player_sleeping_delay_random);
6233
6234       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6235     }
6236
6237     if (game.player_sleeping_delay_fixed +
6238         game.player_sleeping_delay_random > 0 &&
6239         player->anim_delay_counter == 0 &&
6240         player->post_delay_counter == 0 &&
6241         FrameCounter >= player->frame_counter_sleeping)
6242       player->is_sleeping = TRUE;
6243     else if (game.player_boring_delay_fixed +
6244              game.player_boring_delay_random > 0 &&
6245              FrameCounter >= player->frame_counter_bored)
6246       player->is_bored = TRUE;
6247
6248     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6249                               player->is_bored ? ACTION_BORING :
6250                               ACTION_WAITING);
6251
6252     if (player->is_sleeping)
6253     {
6254       if (player->num_special_action_sleeping > 0)
6255       {
6256         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6257         {
6258           int last_special_action = player->special_action_sleeping;
6259           int num_special_action = player->num_special_action_sleeping;
6260           int special_action =
6261             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6262              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6263              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6264              last_special_action + 1 : ACTION_SLEEPING);
6265           int special_graphic =
6266             el_act_dir2img(player->element_nr, special_action, move_dir);
6267
6268           player->anim_delay_counter =
6269             graphic_info[special_graphic].anim_delay_fixed +
6270             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6271           player->post_delay_counter =
6272             graphic_info[special_graphic].post_delay_fixed +
6273             SimpleRND(graphic_info[special_graphic].post_delay_random);
6274
6275           player->special_action_sleeping = special_action;
6276         }
6277
6278         if (player->anim_delay_counter > 0)
6279         {
6280           player->action_waiting = player->special_action_sleeping;
6281           player->anim_delay_counter--;
6282         }
6283         else if (player->post_delay_counter > 0)
6284         {
6285           player->post_delay_counter--;
6286         }
6287       }
6288     }
6289     else if (player->is_bored)
6290     {
6291       if (player->num_special_action_bored > 0)
6292       {
6293         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6294         {
6295           int special_action =
6296             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6297           int special_graphic =
6298             el_act_dir2img(player->element_nr, special_action, move_dir);
6299
6300           player->anim_delay_counter =
6301             graphic_info[special_graphic].anim_delay_fixed +
6302             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6303           player->post_delay_counter =
6304             graphic_info[special_graphic].post_delay_fixed +
6305             SimpleRND(graphic_info[special_graphic].post_delay_random);
6306
6307           player->special_action_bored = special_action;
6308         }
6309
6310         if (player->anim_delay_counter > 0)
6311         {
6312           player->action_waiting = player->special_action_bored;
6313           player->anim_delay_counter--;
6314         }
6315         else if (player->post_delay_counter > 0)
6316         {
6317           player->post_delay_counter--;
6318         }
6319       }
6320     }
6321   }
6322   else if (last_waiting)        /* waiting -> not waiting */
6323   {
6324     player->is_waiting = FALSE;
6325     player->is_bored = FALSE;
6326     player->is_sleeping = FALSE;
6327
6328     player->frame_counter_bored = -1;
6329     player->frame_counter_sleeping = -1;
6330
6331     player->anim_delay_counter = 0;
6332     player->post_delay_counter = 0;
6333
6334     player->action_waiting = ACTION_DEFAULT;
6335
6336     player->special_action_bored = ACTION_DEFAULT;
6337     player->special_action_sleeping = ACTION_DEFAULT;
6338   }
6339 }
6340
6341 #if 1
6342 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6343 {
6344 #if 0
6345   static byte stored_player_action[MAX_PLAYERS];
6346   static int num_stored_actions = 0;
6347 #endif
6348   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6349   int left      = player_action & JOY_LEFT;
6350   int right     = player_action & JOY_RIGHT;
6351   int up        = player_action & JOY_UP;
6352   int down      = player_action & JOY_DOWN;
6353   int button1   = player_action & JOY_BUTTON_1;
6354   int button2   = player_action & JOY_BUTTON_2;
6355   int dx        = (left ? -1    : right ? 1     : 0);
6356   int dy        = (up   ? -1    : down  ? 1     : 0);
6357
6358 #if 0
6359   stored_player_action[player->index_nr] = 0;
6360   num_stored_actions++;
6361 #endif
6362
6363 #if 0
6364   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6365 #endif
6366
6367   if (!player->active || tape.pausing)
6368     return 0;
6369
6370   if (player_action)
6371   {
6372 #if 0
6373     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6374 #endif
6375
6376     if (button1)
6377       snapped = SnapField(player, dx, dy);
6378     else
6379     {
6380       if (button2)
6381         dropped = DropElement(player);
6382
6383       moved = MovePlayer(player, dx, dy);
6384     }
6385
6386     if (tape.single_step && tape.recording && !tape.pausing)
6387     {
6388       if (button1 || (dropped && !moved))
6389       {
6390         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6391         SnapField(player, 0, 0);                /* stop snapping */
6392       }
6393     }
6394
6395     SetPlayerWaiting(player, FALSE);
6396
6397 #if 1
6398     return player_action;
6399 #else
6400     stored_player_action[player->index_nr] = player_action;
6401 #endif
6402   }
6403   else
6404   {
6405 #if 0
6406     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6407 #endif
6408
6409     /* no actions for this player (no input at player's configured device) */
6410
6411     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6412     SnapField(player, 0, 0);
6413     CheckGravityMovement(player);
6414
6415     if (player->MovPos == 0)
6416       SetPlayerWaiting(player, TRUE);
6417
6418     if (player->MovPos == 0)    /* needed for tape.playing */
6419       player->is_moving = FALSE;
6420
6421     return 0;
6422   }
6423
6424 #if 0
6425   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6426   {
6427     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6428
6429     TapeRecordAction(stored_player_action);
6430     num_stored_actions = 0;
6431   }
6432 #endif
6433 }
6434
6435 #else
6436
6437 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6438 {
6439   static byte stored_player_action[MAX_PLAYERS];
6440   static int num_stored_actions = 0;
6441   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6442   int left      = player_action & JOY_LEFT;
6443   int right     = player_action & JOY_RIGHT;
6444   int up        = player_action & JOY_UP;
6445   int down      = player_action & JOY_DOWN;
6446   int button1   = player_action & JOY_BUTTON_1;
6447   int button2   = player_action & JOY_BUTTON_2;
6448   int dx        = (left ? -1    : right ? 1     : 0);
6449   int dy        = (up   ? -1    : down  ? 1     : 0);
6450
6451   stored_player_action[player->index_nr] = 0;
6452   num_stored_actions++;
6453
6454   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6455
6456   if (!player->active || tape.pausing)
6457     return;
6458
6459   if (player_action)
6460   {
6461     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6462
6463     if (button1)
6464       snapped = SnapField(player, dx, dy);
6465     else
6466     {
6467       if (button2)
6468         dropped = DropElement(player);
6469
6470       moved = MovePlayer(player, dx, dy);
6471     }
6472
6473     if (tape.single_step && tape.recording && !tape.pausing)
6474     {
6475       if (button1 || (dropped && !moved))
6476       {
6477         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6478         SnapField(player, 0, 0);                /* stop snapping */
6479       }
6480     }
6481
6482     stored_player_action[player->index_nr] = player_action;
6483   }
6484   else
6485   {
6486     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6487
6488     /* no actions for this player (no input at player's configured device) */
6489
6490     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6491     SnapField(player, 0, 0);
6492     CheckGravityMovement(player);
6493
6494     if (player->MovPos == 0)
6495       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6496
6497     if (player->MovPos == 0)    /* needed for tape.playing */
6498       player->is_moving = FALSE;
6499   }
6500
6501   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6502   {
6503     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6504
6505     TapeRecordAction(stored_player_action);
6506     num_stored_actions = 0;
6507   }
6508 }
6509 #endif
6510
6511 void GameActions()
6512 {
6513   static unsigned long action_delay = 0;
6514   unsigned long action_delay_value;
6515   int magic_wall_x = 0, magic_wall_y = 0;
6516   int i, x, y, element, graphic;
6517   byte *recorded_player_action;
6518   byte summarized_player_action = 0;
6519 #if 1
6520   byte tape_action[MAX_PLAYERS];
6521 #endif
6522
6523   if (game_status != GAME_MODE_PLAYING)
6524     return;
6525
6526   action_delay_value =
6527     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6528
6529   if (tape.playing && tape.index_search && !tape.pausing)
6530     action_delay_value = 0;
6531
6532   /* ---------- main game synchronization point ---------- */
6533
6534   WaitUntilDelayReached(&action_delay, action_delay_value);
6535
6536   if (network_playing && !network_player_action_received)
6537   {
6538     /*
6539 #ifdef DEBUG
6540     printf("DEBUG: try to get network player actions in time\n");
6541 #endif
6542     */
6543
6544 #if defined(PLATFORM_UNIX)
6545     /* last chance to get network player actions without main loop delay */
6546     HandleNetworking();
6547 #endif
6548
6549     if (game_status != GAME_MODE_PLAYING)
6550       return;
6551
6552     if (!network_player_action_received)
6553     {
6554       /*
6555 #ifdef DEBUG
6556       printf("DEBUG: failed to get network player actions in time\n");
6557 #endif
6558       */
6559       return;
6560     }
6561   }
6562
6563   if (tape.pausing)
6564     return;
6565
6566 #if 0
6567   printf("::: getting new tape action [%d]\n", FrameCounter);
6568 #endif
6569
6570   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6571
6572   for (i = 0; i < MAX_PLAYERS; i++)
6573   {
6574     summarized_player_action |= stored_player[i].action;
6575
6576     if (!network_playing)
6577       stored_player[i].effective_action = stored_player[i].action;
6578   }
6579
6580 #if defined(PLATFORM_UNIX)
6581   if (network_playing)
6582     SendToServer_MovePlayer(summarized_player_action);
6583 #endif
6584
6585   if (!options.network && !setup.team_mode)
6586     local_player->effective_action = summarized_player_action;
6587
6588   for (i = 0; i < MAX_PLAYERS; i++)
6589   {
6590     int actual_player_action = stored_player[i].effective_action;
6591
6592     if (stored_player[i].programmed_action)
6593       actual_player_action = stored_player[i].programmed_action;
6594
6595     if (recorded_player_action)
6596       actual_player_action = recorded_player_action[i];
6597
6598     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6599
6600     if (tape.recording && tape_action[i] && !tape.player_participates[i])
6601       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
6602
6603     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6604   }
6605
6606 #if 1
6607   if (tape.recording)
6608     TapeRecordAction(tape_action);
6609 #endif
6610
6611   network_player_action_received = FALSE;
6612
6613   ScrollScreen(NULL, SCROLL_GO_ON);
6614
6615 #if 0
6616   FrameCounter++;
6617   TimeFrames++;
6618
6619   for (i = 0; i < MAX_PLAYERS; i++)
6620     stored_player[i].Frame++;
6621 #endif
6622
6623 #if 1
6624   if (game.engine_version < VERSION_IDENT(2,2,0,7))
6625   {
6626     for (i = 0; i < MAX_PLAYERS; i++)
6627     {
6628       struct PlayerInfo *player = &stored_player[i];
6629       int x = player->jx;
6630       int y = player->jy;
6631
6632       if (player->active && player->is_pushing && player->is_moving &&
6633           IS_MOVING(x, y))
6634       {
6635         ContinueMoving(x, y);
6636
6637         /* continue moving after pushing (this is actually a bug) */
6638         if (!IS_MOVING(x, y))
6639         {
6640           Stop[x][y] = FALSE;
6641         }
6642       }
6643     }
6644   }
6645 #endif
6646
6647   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6648   {
6649     Changed[x][y] = CE_BITMASK_DEFAULT;
6650     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6651
6652 #if DEBUG
6653     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6654     {
6655       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6656       printf("GameActions(): This should never happen!\n");
6657
6658       ChangePage[x][y] = -1;
6659     }
6660 #endif
6661
6662     Stop[x][y] = FALSE;
6663     if (WasJustMoving[x][y] > 0)
6664       WasJustMoving[x][y]--;
6665     if (WasJustFalling[x][y] > 0)
6666       WasJustFalling[x][y]--;
6667
6668     GfxFrame[x][y]++;
6669
6670 #if 1
6671     /* reset finished pushing action (not done in ContinueMoving() to allow
6672        continous pushing animation for elements with zero push delay) */
6673     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6674     {
6675       ResetGfxAnimation(x, y);
6676       DrawLevelField(x, y);
6677     }
6678 #endif
6679
6680 #if DEBUG
6681     if (IS_BLOCKED(x, y))
6682     {
6683       int oldx, oldy;
6684
6685       Blocked2Moving(x, y, &oldx, &oldy);
6686       if (!IS_MOVING(oldx, oldy))
6687       {
6688         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6689         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6690         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6691         printf("GameActions(): This should never happen!\n");
6692       }
6693     }
6694 #endif
6695   }
6696
6697   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6698   {
6699     element = Feld[x][y];
6700 #if 1
6701     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6702 #else
6703     graphic = el2img(element);
6704 #endif
6705
6706 #if 0
6707     if (element == -1)
6708     {
6709       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6710
6711       element = graphic = 0;
6712     }
6713 #endif
6714
6715     if (graphic_info[graphic].anim_global_sync)
6716       GfxFrame[x][y] = FrameCounter;
6717
6718     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6719         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6720       ResetRandomAnimationValue(x, y);
6721
6722     SetRandomAnimationValue(x, y);
6723
6724 #if 1
6725     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6726 #endif
6727
6728     if (IS_INACTIVE(element))
6729     {
6730       if (IS_ANIMATED(graphic))
6731         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6732
6733       continue;
6734     }
6735
6736 #if 1
6737     /* this may take place after moving, so 'element' may have changed */
6738 #if 0
6739     if (IS_CHANGING(x, y))
6740 #else
6741     if (IS_CHANGING(x, y) &&
6742         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6743 #endif
6744     {
6745 #if 0
6746       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6747                     element_info[element].event_page_nr[CE_DELAY]);
6748 #else
6749       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6750 #endif
6751
6752       element = Feld[x][y];
6753       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6754     }
6755 #endif
6756
6757     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6758     {
6759       StartMoving(x, y);
6760
6761 #if 1
6762       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6763 #if 0
6764       if (element == EL_MOLE)
6765         printf("::: %d, %d, %d [%d]\n",
6766                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6767                GfxAction[x][y]);
6768 #endif
6769 #if 0
6770       if (element == EL_YAMYAM)
6771         printf("::: %d, %d, %d\n",
6772                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6773 #endif
6774 #endif
6775
6776       if (IS_ANIMATED(graphic) &&
6777           !IS_MOVING(x, y) &&
6778           !Stop[x][y])
6779       {
6780         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6781
6782 #if 0
6783         if (element == EL_BUG)
6784           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6785 #endif
6786
6787 #if 0
6788         if (element == EL_MOLE)
6789           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6790 #endif
6791       }
6792
6793       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6794         EdelsteinFunkeln(x, y);
6795     }
6796     else if ((element == EL_ACID ||
6797               element == EL_EXIT_OPEN ||
6798               element == EL_SP_EXIT_OPEN ||
6799               element == EL_SP_TERMINAL ||
6800               element == EL_SP_TERMINAL_ACTIVE ||
6801               element == EL_EXTRA_TIME ||
6802               element == EL_SHIELD_NORMAL ||
6803               element == EL_SHIELD_DEADLY) &&
6804              IS_ANIMATED(graphic))
6805       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6806     else if (IS_MOVING(x, y))
6807       ContinueMoving(x, y);
6808     else if (IS_ACTIVE_BOMB(element))
6809       CheckDynamite(x, y);
6810 #if 0
6811     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6812       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6813 #endif
6814     else if (element == EL_AMOEBA_GROWING)
6815       AmoebeWaechst(x, y);
6816     else if (element == EL_AMOEBA_SHRINKING)
6817       AmoebaDisappearing(x, y);
6818
6819 #if !USE_NEW_AMOEBA_CODE
6820     else if (IS_AMOEBALIVE(element))
6821       AmoebeAbleger(x, y);
6822 #endif
6823
6824     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6825       Life(x, y);
6826     else if (element == EL_EXIT_CLOSED)
6827       CheckExit(x, y);
6828     else if (element == EL_SP_EXIT_CLOSED)
6829       CheckExitSP(x, y);
6830     else if (element == EL_EXPANDABLE_WALL_GROWING)
6831       MauerWaechst(x, y);
6832     else if (element == EL_EXPANDABLE_WALL ||
6833              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6834              element == EL_EXPANDABLE_WALL_VERTICAL ||
6835              element == EL_EXPANDABLE_WALL_ANY)
6836       MauerAbleger(x, y);
6837     else if (element == EL_FLAMES)
6838       CheckForDragon(x, y);
6839 #if 0
6840     else if (IS_AUTO_CHANGING(element))
6841       ChangeElement(x, y);
6842 #endif
6843     else if (element == EL_EXPLOSION)
6844       ; /* drawing of correct explosion animation is handled separately */
6845     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6846       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6847
6848 #if 0
6849     /* this may take place after moving, so 'element' may have changed */
6850     if (IS_AUTO_CHANGING(Feld[x][y]))
6851       ChangeElement(x, y);
6852 #endif
6853
6854     if (IS_BELT_ACTIVE(element))
6855       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6856
6857     if (game.magic_wall_active)
6858     {
6859       int jx = local_player->jx, jy = local_player->jy;
6860
6861       /* play the element sound at the position nearest to the player */
6862       if ((element == EL_MAGIC_WALL_FULL ||
6863            element == EL_MAGIC_WALL_ACTIVE ||
6864            element == EL_MAGIC_WALL_EMPTYING ||
6865            element == EL_BD_MAGIC_WALL_FULL ||
6866            element == EL_BD_MAGIC_WALL_ACTIVE ||
6867            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6868           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6869       {
6870         magic_wall_x = x;
6871         magic_wall_y = y;
6872       }
6873     }
6874   }
6875
6876 #if USE_NEW_AMOEBA_CODE
6877   /* new experimental amoeba growth stuff */
6878 #if 1
6879   if (!(FrameCounter % 8))
6880 #endif
6881   {
6882     static unsigned long random = 1684108901;
6883
6884     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6885     {
6886 #if 0
6887       x = (random >> 10) % lev_fieldx;
6888       y = (random >> 20) % lev_fieldy;
6889 #else
6890       x = RND(lev_fieldx);
6891       y = RND(lev_fieldy);
6892 #endif
6893       element = Feld[x][y];
6894
6895       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6896       if (!IS_PLAYER(x,y) &&
6897           (element == EL_EMPTY ||
6898            element == EL_SAND ||
6899            element == EL_QUICKSAND_EMPTY ||
6900            element == EL_ACID_SPLASH_LEFT ||
6901            element == EL_ACID_SPLASH_RIGHT))
6902       {
6903         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6904             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6905             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6906             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6907           Feld[x][y] = EL_AMOEBA_DROP;
6908       }
6909
6910       random = random * 129 + 1;
6911     }
6912   }
6913 #endif
6914
6915 #if 0
6916   if (game.explosions_delayed)
6917 #endif
6918   {
6919     game.explosions_delayed = FALSE;
6920
6921     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6922     {
6923       element = Feld[x][y];
6924
6925       if (ExplodeField[x][y])
6926         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6927       else if (element == EL_EXPLOSION)
6928         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6929
6930       ExplodeField[x][y] = EX_NO_EXPLOSION;
6931     }
6932
6933     game.explosions_delayed = TRUE;
6934   }
6935
6936   if (game.magic_wall_active)
6937   {
6938     if (!(game.magic_wall_time_left % 4))
6939     {
6940       int element = Feld[magic_wall_x][magic_wall_y];
6941
6942       if (element == EL_BD_MAGIC_WALL_FULL ||
6943           element == EL_BD_MAGIC_WALL_ACTIVE ||
6944           element == EL_BD_MAGIC_WALL_EMPTYING)
6945         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6946       else
6947         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6948     }
6949
6950     if (game.magic_wall_time_left > 0)
6951     {
6952       game.magic_wall_time_left--;
6953       if (!game.magic_wall_time_left)
6954       {
6955         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6956         {
6957           element = Feld[x][y];
6958
6959           if (element == EL_MAGIC_WALL_ACTIVE ||
6960               element == EL_MAGIC_WALL_FULL)
6961           {
6962             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6963             DrawLevelField(x, y);
6964           }
6965           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6966                    element == EL_BD_MAGIC_WALL_FULL)
6967           {
6968             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6969             DrawLevelField(x, y);
6970           }
6971         }
6972
6973         game.magic_wall_active = FALSE;
6974       }
6975     }
6976   }
6977
6978   if (game.light_time_left > 0)
6979   {
6980     game.light_time_left--;
6981
6982     if (game.light_time_left == 0)
6983       RedrawAllLightSwitchesAndInvisibleElements();
6984   }
6985
6986   if (game.timegate_time_left > 0)
6987   {
6988     game.timegate_time_left--;
6989
6990     if (game.timegate_time_left == 0)
6991       CloseAllOpenTimegates();
6992   }
6993
6994   for (i = 0; i < MAX_PLAYERS; i++)
6995   {
6996     struct PlayerInfo *player = &stored_player[i];
6997
6998     if (SHIELD_ON(player))
6999     {
7000       if (player->shield_deadly_time_left)
7001         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7002       else if (player->shield_normal_time_left)
7003         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7004     }
7005   }
7006
7007   if (TimeFrames >= FRAMES_PER_SECOND)
7008   {
7009     TimeFrames = 0;
7010     TimePlayed++;
7011
7012     for (i = 0; i < MAX_PLAYERS; i++)
7013     {
7014       struct PlayerInfo *player = &stored_player[i];
7015
7016       if (SHIELD_ON(player))
7017       {
7018         player->shield_normal_time_left--;
7019
7020         if (player->shield_deadly_time_left > 0)
7021           player->shield_deadly_time_left--;
7022       }
7023     }
7024
7025     if (tape.recording || tape.playing)
7026       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7027
7028     if (TimeLeft > 0)
7029     {
7030       TimeLeft--;
7031
7032       if (TimeLeft <= 10 && setup.time_limit)
7033         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7034
7035       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7036
7037       if (!TimeLeft && setup.time_limit)
7038         for (i = 0; i < MAX_PLAYERS; i++)
7039           KillHero(&stored_player[i]);
7040     }
7041     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7042       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7043   }
7044
7045   DrawAllPlayers();
7046   PlayAllPlayersSound();
7047
7048   if (options.debug)                    /* calculate frames per second */
7049   {
7050     static unsigned long fps_counter = 0;
7051     static int fps_frames = 0;
7052     unsigned long fps_delay_ms = Counter() - fps_counter;
7053
7054     fps_frames++;
7055
7056     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7057     {
7058       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7059
7060       fps_frames = 0;
7061       fps_counter = Counter();
7062     }
7063
7064     redraw_mask |= REDRAW_FPS;
7065   }
7066
7067 #if 0
7068   if (stored_player[0].jx != stored_player[0].last_jx ||
7069       stored_player[0].jy != stored_player[0].last_jy)
7070     printf("::: %d, %d, %d, %d, %d\n",
7071            stored_player[0].MovDir,
7072            stored_player[0].MovPos,
7073            stored_player[0].GfxPos,
7074            stored_player[0].Frame,
7075            stored_player[0].StepFrame);
7076 #endif
7077
7078 #if 1
7079   FrameCounter++;
7080   TimeFrames++;
7081
7082   for (i = 0; i < MAX_PLAYERS; i++)
7083   {
7084     int move_frames =
7085       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
7086
7087     stored_player[i].Frame += move_frames;
7088
7089     if (stored_player[i].MovPos != 0)
7090       stored_player[i].StepFrame += move_frames;
7091   }
7092 #endif
7093
7094 #if 1
7095   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7096   {
7097     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7098
7099     local_player->show_envelope = 0;
7100   }
7101 #endif
7102 }
7103
7104 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7105 {
7106   int min_x = x, min_y = y, max_x = x, max_y = y;
7107   int i;
7108
7109   for (i = 0; i < MAX_PLAYERS; i++)
7110   {
7111     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7112
7113     if (!stored_player[i].active || &stored_player[i] == player)
7114       continue;
7115
7116     min_x = MIN(min_x, jx);
7117     min_y = MIN(min_y, jy);
7118     max_x = MAX(max_x, jx);
7119     max_y = MAX(max_y, jy);
7120   }
7121
7122   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7123 }
7124
7125 static boolean AllPlayersInVisibleScreen()
7126 {
7127   int i;
7128
7129   for (i = 0; i < MAX_PLAYERS; i++)
7130   {
7131     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7132
7133     if (!stored_player[i].active)
7134       continue;
7135
7136     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7137       return FALSE;
7138   }
7139
7140   return TRUE;
7141 }
7142
7143 void ScrollLevel(int dx, int dy)
7144 {
7145   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7146   int x, y;
7147
7148   BlitBitmap(drawto_field, drawto_field,
7149              FX + TILEX * (dx == -1) - softscroll_offset,
7150              FY + TILEY * (dy == -1) - softscroll_offset,
7151              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7152              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7153              FX + TILEX * (dx == 1) - softscroll_offset,
7154              FY + TILEY * (dy == 1) - softscroll_offset);
7155
7156   if (dx)
7157   {
7158     x = (dx == 1 ? BX1 : BX2);
7159     for (y = BY1; y <= BY2; y++)
7160       DrawScreenField(x, y);
7161   }
7162
7163   if (dy)
7164   {
7165     y = (dy == 1 ? BY1 : BY2);
7166     for (x = BX1; x <= BX2; x++)
7167       DrawScreenField(x, y);
7168   }
7169
7170   redraw_mask |= REDRAW_FIELD;
7171 }
7172
7173 static void CheckGravityMovement(struct PlayerInfo *player)
7174 {
7175   if (game.gravity && !player->programmed_action)
7176   {
7177     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7178     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7179     int move_dir =
7180       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7181        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7182        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7183     int jx = player->jx, jy = player->jy;
7184     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7185     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7186     int new_jx = jx + dx, new_jy = jy + dy;
7187     boolean field_under_player_is_free =
7188       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7189     boolean player_is_moving_to_valid_field =
7190       (IN_LEV_FIELD(new_jx, new_jy) &&
7191        (Feld[new_jx][new_jy] == EL_SP_BASE ||
7192         Feld[new_jx][new_jy] == EL_SAND));
7193     /* !!! extend EL_SAND to anything diggable !!! */
7194
7195     if (field_under_player_is_free &&
7196         !player_is_moving_to_valid_field &&
7197         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7198       player->programmed_action = MV_DOWN;
7199   }
7200 }
7201
7202 /*
7203   MovePlayerOneStep()
7204   -----------------------------------------------------------------------------
7205   dx, dy:               direction (non-diagonal) to try to move the player to
7206   real_dx, real_dy:     direction as read from input device (can be diagonal)
7207 */
7208
7209 boolean MovePlayerOneStep(struct PlayerInfo *player,
7210                           int dx, int dy, int real_dx, int real_dy)
7211 {
7212 #if 0
7213   static int change_sides[4][2] =
7214   {
7215     /* enter side        leave side */
7216     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
7217     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
7218     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
7219     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
7220   };
7221   int move_direction = (dx == -1 ? MV_LEFT :
7222                         dx == +1 ? MV_RIGHT :
7223                         dy == -1 ? MV_UP :
7224                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7225   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7226   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7227 #endif
7228   int jx = player->jx, jy = player->jy;
7229   int new_jx = jx + dx, new_jy = jy + dy;
7230   int element;
7231   int can_move;
7232
7233   if (!player->active || (!dx && !dy))
7234     return MF_NO_ACTION;
7235
7236   player->MovDir = (dx < 0 ? MV_LEFT :
7237                     dx > 0 ? MV_RIGHT :
7238                     dy < 0 ? MV_UP :
7239                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
7240
7241   if (!IN_LEV_FIELD(new_jx, new_jy))
7242     return MF_NO_ACTION;
7243
7244   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7245     return MF_NO_ACTION;
7246
7247 #if 0
7248   element = MovingOrBlocked2Element(new_jx, new_jy);
7249 #else
7250   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7251 #endif
7252
7253   if (DONT_RUN_INTO(element))
7254   {
7255     if (element == EL_ACID && dx == 0 && dy == 1)
7256     {
7257       SplashAcid(jx, jy);
7258       Feld[jx][jy] = EL_PLAYER_1;
7259       InitMovingField(jx, jy, MV_DOWN);
7260       Store[jx][jy] = EL_ACID;
7261       ContinueMoving(jx, jy);
7262       BuryHero(player);
7263     }
7264     else
7265       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7266
7267     return MF_MOVING;
7268   }
7269
7270   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7271   if (can_move != MF_MOVING)
7272     return can_move;
7273
7274   /* check if DigField() has caused relocation of the player */
7275   if (player->jx != jx || player->jy != jy)
7276     return MF_NO_ACTION;
7277
7278   StorePlayer[jx][jy] = 0;
7279   player->last_jx = jx;
7280   player->last_jy = jy;
7281   player->jx = new_jx;
7282   player->jy = new_jy;
7283   StorePlayer[new_jx][new_jy] = player->element_nr;
7284
7285   player->MovPos =
7286     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7287
7288   player->step_counter++;
7289
7290   PlayerVisit[jx][jy] = FrameCounter;
7291
7292   ScrollPlayer(player, SCROLL_INIT);
7293
7294 #if 0
7295   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7296   {
7297     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7298                                     CE_OTHER_GETS_LEFT);
7299     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7300                            CE_LEFT_BY_PLAYER, -1);
7301   }
7302
7303   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7304   {
7305     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7306                                     enter_side, CE_OTHER_GETS_ENTERED);
7307     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7308                            CE_ENTERED_BY_PLAYER, -1);
7309   }
7310 #endif
7311
7312   return MF_MOVING;
7313 }
7314
7315 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7316 {
7317   int jx = player->jx, jy = player->jy;
7318   int old_jx = jx, old_jy = jy;
7319   int moved = MF_NO_ACTION;
7320
7321   if (!player->active || (!dx && !dy))
7322     return FALSE;
7323
7324 #if 0
7325   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7326       !tape.playing)
7327     return FALSE;
7328 #else
7329   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7330       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7331     return FALSE;
7332 #endif
7333
7334   /* remove the last programmed player action */
7335   player->programmed_action = 0;
7336
7337   if (player->MovPos)
7338   {
7339     /* should only happen if pre-1.2 tape recordings are played */
7340     /* this is only for backward compatibility */
7341
7342     int original_move_delay_value = player->move_delay_value;
7343
7344 #if DEBUG
7345     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7346            tape.counter);
7347 #endif
7348
7349     /* scroll remaining steps with finest movement resolution */
7350     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7351
7352     while (player->MovPos)
7353     {
7354       ScrollPlayer(player, SCROLL_GO_ON);
7355       ScrollScreen(NULL, SCROLL_GO_ON);
7356       FrameCounter++;
7357       DrawAllPlayers();
7358       BackToFront();
7359     }
7360
7361     player->move_delay_value = original_move_delay_value;
7362   }
7363
7364   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7365   {
7366     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7367       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7368   }
7369   else
7370   {
7371     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7372       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7373   }
7374
7375   jx = player->jx;
7376   jy = player->jy;
7377
7378   if (moved & MF_MOVING && !ScreenMovPos &&
7379       (player == local_player || !options.network))
7380   {
7381     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7382     int offset = (setup.scroll_delay ? 3 : 0);
7383
7384     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7385     {
7386       /* actual player has left the screen -- scroll in that direction */
7387       if (jx != old_jx)         /* player has moved horizontally */
7388         scroll_x += (jx - old_jx);
7389       else                      /* player has moved vertically */
7390         scroll_y += (jy - old_jy);
7391     }
7392     else
7393     {
7394       if (jx != old_jx)         /* player has moved horizontally */
7395       {
7396         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7397             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7398           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7399
7400         /* don't scroll over playfield boundaries */
7401         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7402           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7403
7404         /* don't scroll more than one field at a time */
7405         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7406
7407         /* don't scroll against the player's moving direction */
7408         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7409             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7410           scroll_x = old_scroll_x;
7411       }
7412       else                      /* player has moved vertically */
7413       {
7414         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7415             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7416           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7417
7418         /* don't scroll over playfield boundaries */
7419         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7420           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7421
7422         /* don't scroll more than one field at a time */
7423         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7424
7425         /* don't scroll against the player's moving direction */
7426         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7427             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7428           scroll_y = old_scroll_y;
7429       }
7430     }
7431
7432     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7433     {
7434       if (!options.network && !AllPlayersInVisibleScreen())
7435       {
7436         scroll_x = old_scroll_x;
7437         scroll_y = old_scroll_y;
7438       }
7439       else
7440       {
7441         ScrollScreen(player, SCROLL_INIT);
7442         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7443       }
7444     }
7445   }
7446
7447 #if 0
7448 #if 1
7449   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7450 #else
7451   if (!(moved & MF_MOVING) && !player->is_pushing)
7452     player->Frame = 0;
7453 #endif
7454 #endif
7455
7456   player->StepFrame = 0;
7457
7458   if (moved & MF_MOVING)
7459   {
7460     if (old_jx != jx && old_jy == jy)
7461       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7462     else if (old_jx == jx && old_jy != jy)
7463       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7464
7465     DrawLevelField(jx, jy);     /* for "crumbled sand" */
7466
7467     player->last_move_dir = player->MovDir;
7468     player->is_moving = TRUE;
7469 #if 1
7470     player->is_snapping = FALSE;
7471 #endif
7472
7473 #if 1
7474     player->is_switching = FALSE;
7475 #endif
7476
7477
7478 #if 1
7479     {
7480       static int change_sides[4][2] =
7481       {
7482         /* enter side           leave side */
7483         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
7484         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
7485         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
7486         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
7487       };
7488       int move_direction = player->MovDir;
7489       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7490       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7491
7492 #if 1
7493       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7494       {
7495         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7496                                         leave_side, CE_OTHER_GETS_LEFT);
7497         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7498                                leave_side, CE_LEFT_BY_PLAYER, -1);
7499       }
7500
7501       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7502       {
7503         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7504                                         enter_side, CE_OTHER_GETS_ENTERED);
7505         CheckElementSideChange(jx, jy, Feld[jx][jy],
7506                                enter_side, CE_ENTERED_BY_PLAYER, -1);
7507       }
7508 #endif
7509
7510     }
7511 #endif
7512
7513
7514   }
7515   else
7516   {
7517     CheckGravityMovement(player);
7518
7519     /*
7520     player->last_move_dir = MV_NO_MOVING;
7521     */
7522     player->is_moving = FALSE;
7523   }
7524
7525   if (game.engine_version < VERSION_IDENT(3,0,7,0))
7526   {
7527     TestIfHeroTouchesBadThing(jx, jy);
7528     TestIfPlayerTouchesCustomElement(jx, jy);
7529   }
7530
7531   if (!player->active)
7532     RemoveHero(player);
7533
7534   return moved;
7535 }
7536
7537 void ScrollPlayer(struct PlayerInfo *player, int mode)
7538 {
7539   int jx = player->jx, jy = player->jy;
7540   int last_jx = player->last_jx, last_jy = player->last_jy;
7541   int move_stepsize = TILEX / player->move_delay_value;
7542
7543   if (!player->active || !player->MovPos)
7544     return;
7545
7546   if (mode == SCROLL_INIT)
7547   {
7548     player->actual_frame_counter = FrameCounter;
7549     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7550
7551     if (Feld[last_jx][last_jy] == EL_EMPTY)
7552       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7553
7554 #if 0
7555     DrawPlayer(player);
7556 #endif
7557     return;
7558   }
7559   else if (!FrameReached(&player->actual_frame_counter, 1))
7560     return;
7561
7562   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7563   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7564
7565   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7566     Feld[last_jx][last_jy] = EL_EMPTY;
7567
7568   /* before DrawPlayer() to draw correct player graphic for this case */
7569   if (player->MovPos == 0)
7570     CheckGravityMovement(player);
7571
7572 #if 0
7573   DrawPlayer(player);   /* needed here only to cleanup last field */
7574 #endif
7575
7576   if (player->MovPos == 0)      /* player reached destination field */
7577   {
7578     if (IS_PASSABLE(Feld[last_jx][last_jy]))
7579     {
7580       /* continue with normal speed after quickly moving through gate */
7581       HALVE_PLAYER_SPEED(player);
7582
7583       /* be able to make the next move without delay */
7584       player->move_delay = 0;
7585     }
7586
7587     player->last_jx = jx;
7588     player->last_jy = jy;
7589
7590     if (Feld[jx][jy] == EL_EXIT_OPEN ||
7591         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7592         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
7593     {
7594       DrawPlayer(player);       /* needed here only to cleanup last field */
7595       RemoveHero(player);
7596
7597       if (local_player->friends_still_needed == 0 ||
7598           IS_SP_ELEMENT(Feld[jx][jy]))
7599         player->LevelSolved = player->GameOver = TRUE;
7600     }
7601
7602     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7603     {
7604       TestIfHeroTouchesBadThing(jx, jy);
7605       TestIfPlayerTouchesCustomElement(jx, jy);
7606 #if 1
7607       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
7608 #endif
7609
7610       if (!player->active)
7611         RemoveHero(player);
7612     }
7613
7614     if (tape.single_step && tape.recording && !tape.pausing &&
7615         !player->programmed_action)
7616       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7617   }
7618 }
7619
7620 void ScrollScreen(struct PlayerInfo *player, int mode)
7621 {
7622   static unsigned long screen_frame_counter = 0;
7623
7624   if (mode == SCROLL_INIT)
7625   {
7626     /* set scrolling step size according to actual player's moving speed */
7627     ScrollStepSize = TILEX / player->move_delay_value;
7628
7629     screen_frame_counter = FrameCounter;
7630     ScreenMovDir = player->MovDir;
7631     ScreenMovPos = player->MovPos;
7632     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7633     return;
7634   }
7635   else if (!FrameReached(&screen_frame_counter, 1))
7636     return;
7637
7638   if (ScreenMovPos)
7639   {
7640     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7641     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7642     redraw_mask |= REDRAW_FIELD;
7643   }
7644   else
7645     ScreenMovDir = MV_NO_MOVING;
7646 }
7647
7648 void TestIfPlayerTouchesCustomElement(int x, int y)
7649 {
7650   static int xy[4][2] =
7651   {
7652     { 0, -1 },
7653     { -1, 0 },
7654     { +1, 0 },
7655     { 0, +1 }
7656   };
7657   static int change_sides[4][2] =
7658   {
7659     /* center side       border side */
7660     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7661     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7662     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7663     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7664   };
7665   static int touch_dir[4] =
7666   {
7667     MV_LEFT | MV_RIGHT,
7668     MV_UP   | MV_DOWN,
7669     MV_UP   | MV_DOWN,
7670     MV_LEFT | MV_RIGHT
7671   };
7672   int center_element = Feld[x][y];      /* should always be non-moving! */
7673   int i;
7674
7675   for (i = 0; i < 4; i++)
7676   {
7677     int xx = x + xy[i][0];
7678     int yy = y + xy[i][1];
7679     int center_side = change_sides[i][0];
7680     int border_side = change_sides[i][1];
7681     int border_element;
7682
7683     if (!IN_LEV_FIELD(xx, yy))
7684       continue;
7685
7686     if (IS_PLAYER(x, y))
7687     {
7688       if (game.engine_version < VERSION_IDENT(3,0,7,0))
7689         border_element = Feld[xx][yy];          /* may be moving! */
7690       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7691         border_element = Feld[xx][yy];
7692       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7693         border_element = MovingOrBlocked2Element(xx, yy);
7694       else
7695         continue;               /* center and border element do not touch */
7696
7697       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7698                                       CE_OTHER_GETS_TOUCHED);
7699       CheckElementSideChange(xx, yy, border_element, border_side,
7700                              CE_TOUCHED_BY_PLAYER, -1);
7701     }
7702     else if (IS_PLAYER(xx, yy))
7703     {
7704       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7705       {
7706         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7707
7708         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7709           continue;             /* center and border element do not touch */
7710       }
7711
7712       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7713                                       CE_OTHER_GETS_TOUCHED);
7714       CheckElementSideChange(x, y, center_element, center_side,
7715                              CE_TOUCHED_BY_PLAYER, -1);
7716
7717       break;
7718     }
7719   }
7720 }
7721
7722 void TestIfElementTouchesCustomElement(int x, int y)
7723 {
7724   static int xy[4][2] =
7725   {
7726     { 0, -1 },
7727     { -1, 0 },
7728     { +1, 0 },
7729     { 0, +1 }
7730   };
7731   static int change_sides[4][2] =
7732   {
7733     /* center side      border side */
7734     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7735     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7736     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7737     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7738   };
7739   static int touch_dir[4] =
7740   {
7741     MV_LEFT | MV_RIGHT,
7742     MV_UP   | MV_DOWN,
7743     MV_UP   | MV_DOWN,
7744     MV_LEFT | MV_RIGHT
7745   };
7746   boolean change_center_element = FALSE;
7747   int center_element_change_page = 0;
7748   int center_element = Feld[x][y];      /* should always be non-moving! */
7749   int i, j;
7750
7751   for (i = 0; i < 4; i++)
7752   {
7753     int xx = x + xy[i][0];
7754     int yy = y + xy[i][1];
7755     int center_side = change_sides[i][0];
7756     int border_side = change_sides[i][1];
7757     int border_element;
7758
7759     if (!IN_LEV_FIELD(xx, yy))
7760       continue;
7761
7762     if (game.engine_version < VERSION_IDENT(3,0,7,0))
7763       border_element = Feld[xx][yy];    /* may be moving! */
7764     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7765       border_element = Feld[xx][yy];
7766     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7767       border_element = MovingOrBlocked2Element(xx, yy);
7768     else
7769       continue;                 /* center and border element do not touch */
7770
7771     /* check for change of center element (but change it only once) */
7772     if (IS_CUSTOM_ELEMENT(center_element) &&
7773         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7774         !change_center_element)
7775     {
7776       for (j = 0; j < element_info[center_element].num_change_pages; j++)
7777       {
7778         struct ElementChangeInfo *change =
7779           &element_info[center_element].change_page[j];
7780
7781         if (change->can_change &&
7782             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7783             change->sides & border_side &&
7784             change->trigger_element == border_element)
7785         {
7786           change_center_element = TRUE;
7787           center_element_change_page = j;
7788
7789           break;
7790         }
7791       }
7792     }
7793
7794     /* check for change of border element */
7795     if (IS_CUSTOM_ELEMENT(border_element) &&
7796         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7797     {
7798       for (j = 0; j < element_info[border_element].num_change_pages; j++)
7799       {
7800         struct ElementChangeInfo *change =
7801           &element_info[border_element].change_page[j];
7802
7803         if (change->can_change &&
7804             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7805             change->sides & center_side &&
7806             change->trigger_element == center_element)
7807         {
7808           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7809                                  CE_OTHER_IS_TOUCHING, j);
7810           break;
7811         }
7812       }
7813     }
7814   }
7815
7816   if (change_center_element)
7817     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7818                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7819 }
7820
7821 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7822 {
7823   int i, kill_x = -1, kill_y = -1;
7824   static int test_xy[4][2] =
7825   {
7826     { 0, -1 },
7827     { -1, 0 },
7828     { +1, 0 },
7829     { 0, +1 }
7830   };
7831   static int test_dir[4] =
7832   {
7833     MV_UP,
7834     MV_LEFT,
7835     MV_RIGHT,
7836     MV_DOWN
7837   };
7838
7839   for (i = 0; i < 4; i++)
7840   {
7841     int test_x, test_y, test_move_dir, test_element;
7842
7843     test_x = good_x + test_xy[i][0];
7844     test_y = good_y + test_xy[i][1];
7845     if (!IN_LEV_FIELD(test_x, test_y))
7846       continue;
7847
7848     test_move_dir =
7849       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7850
7851 #if 0
7852     test_element = Feld[test_x][test_y];
7853 #else
7854     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7855 #endif
7856
7857     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7858        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7859     */
7860     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7861         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
7862     {
7863       kill_x = test_x;
7864       kill_y = test_y;
7865       break;
7866     }
7867   }
7868
7869   if (kill_x != -1 || kill_y != -1)
7870   {
7871     if (IS_PLAYER(good_x, good_y))
7872     {
7873       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7874
7875       if (player->shield_deadly_time_left > 0)
7876         Bang(kill_x, kill_y);
7877       else if (!PLAYER_PROTECTED(good_x, good_y))
7878         KillHero(player);
7879     }
7880     else
7881       Bang(good_x, good_y);
7882   }
7883 }
7884
7885 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7886 {
7887   int i, kill_x = -1, kill_y = -1;
7888   int bad_element = Feld[bad_x][bad_y];
7889   static int test_xy[4][2] =
7890   {
7891     { 0, -1 },
7892     { -1, 0 },
7893     { +1, 0 },
7894     { 0, +1 }
7895   };
7896   static int touch_dir[4] =
7897   {
7898     MV_LEFT | MV_RIGHT,
7899     MV_UP   | MV_DOWN,
7900     MV_UP   | MV_DOWN,
7901     MV_LEFT | MV_RIGHT
7902   };
7903   static int test_dir[4] =
7904   {
7905     MV_UP,
7906     MV_LEFT,
7907     MV_RIGHT,
7908     MV_DOWN
7909   };
7910
7911   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
7912     return;
7913
7914   for (i = 0; i < 4; i++)
7915   {
7916     int test_x, test_y, test_move_dir, test_element;
7917
7918     test_x = bad_x + test_xy[i][0];
7919     test_y = bad_y + test_xy[i][1];
7920     if (!IN_LEV_FIELD(test_x, test_y))
7921       continue;
7922
7923     test_move_dir =
7924       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7925
7926     test_element = Feld[test_x][test_y];
7927
7928     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7929        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7930     */
7931     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
7932         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
7933     {
7934       /* good thing is player or penguin that does not move away */
7935       if (IS_PLAYER(test_x, test_y))
7936       {
7937         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7938
7939         if (bad_element == EL_ROBOT && player->is_moving)
7940           continue;     /* robot does not kill player if he is moving */
7941
7942         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7943         {
7944           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7945             continue;           /* center and border element do not touch */
7946         }
7947
7948         kill_x = test_x;
7949         kill_y = test_y;
7950         break;
7951       }
7952       else if (test_element == EL_PENGUIN)
7953       {
7954         kill_x = test_x;
7955         kill_y = test_y;
7956         break;
7957       }
7958     }
7959   }
7960
7961   if (kill_x != -1 || kill_y != -1)
7962   {
7963     if (IS_PLAYER(kill_x, kill_y))
7964     {
7965       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7966
7967       if (player->shield_deadly_time_left > 0)
7968         Bang(bad_x, bad_y);
7969       else if (!PLAYER_PROTECTED(kill_x, kill_y))
7970         KillHero(player);
7971     }
7972     else
7973       Bang(kill_x, kill_y);
7974   }
7975 }
7976
7977 void TestIfHeroTouchesBadThing(int x, int y)
7978 {
7979   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7980 }
7981
7982 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7983 {
7984   TestIfGoodThingHitsBadThing(x, y, move_dir);
7985 }
7986
7987 void TestIfBadThingTouchesHero(int x, int y)
7988 {
7989   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7990 }
7991
7992 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7993 {
7994   TestIfBadThingHitsGoodThing(x, y, move_dir);
7995 }
7996
7997 void TestIfFriendTouchesBadThing(int x, int y)
7998 {
7999   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8000 }
8001
8002 void TestIfBadThingTouchesFriend(int x, int y)
8003 {
8004   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8005 }
8006
8007 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8008 {
8009   int i, kill_x = bad_x, kill_y = bad_y;
8010   static int xy[4][2] =
8011   {
8012     { 0, -1 },
8013     { -1, 0 },
8014     { +1, 0 },
8015     { 0, +1 }
8016   };
8017
8018   for (i = 0; i < 4; i++)
8019   {
8020     int x, y, element;
8021
8022     x = bad_x + xy[i][0];
8023     y = bad_y + xy[i][1];
8024     if (!IN_LEV_FIELD(x, y))
8025       continue;
8026
8027     element = Feld[x][y];
8028     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8029         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8030     {
8031       kill_x = x;
8032       kill_y = y;
8033       break;
8034     }
8035   }
8036
8037   if (kill_x != bad_x || kill_y != bad_y)
8038     Bang(bad_x, bad_y);
8039 }
8040
8041 void KillHero(struct PlayerInfo *player)
8042 {
8043   int jx = player->jx, jy = player->jy;
8044
8045   if (!player->active)
8046     return;
8047
8048   /* remove accessible field at the player's position */
8049   Feld[jx][jy] = EL_EMPTY;
8050
8051   /* deactivate shield (else Bang()/Explode() would not work right) */
8052   player->shield_normal_time_left = 0;
8053   player->shield_deadly_time_left = 0;
8054
8055   Bang(jx, jy);
8056   BuryHero(player);
8057 }
8058
8059 static void KillHeroUnlessProtected(int x, int y)
8060 {
8061   if (!PLAYER_PROTECTED(x, y))
8062     KillHero(PLAYERINFO(x, y));
8063 }
8064
8065 void BuryHero(struct PlayerInfo *player)
8066 {
8067   int jx = player->jx, jy = player->jy;
8068
8069   if (!player->active)
8070     return;
8071
8072 #if 1
8073   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8074 #else
8075   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8076 #endif
8077   PlayLevelSound(jx, jy, SND_GAME_LOSING);
8078
8079   player->GameOver = TRUE;
8080   RemoveHero(player);
8081 }
8082
8083 void RemoveHero(struct PlayerInfo *player)
8084 {
8085   int jx = player->jx, jy = player->jy;
8086   int i, found = FALSE;
8087
8088   player->present = FALSE;
8089   player->active = FALSE;
8090
8091   if (!ExplodeField[jx][jy])
8092     StorePlayer[jx][jy] = 0;
8093
8094   for (i = 0; i < MAX_PLAYERS; i++)
8095     if (stored_player[i].active)
8096       found = TRUE;
8097
8098   if (!found)
8099     AllPlayersGone = TRUE;
8100
8101   ExitX = ZX = jx;
8102   ExitY = ZY = jy;
8103 }
8104
8105 /*
8106   =============================================================================
8107   checkDiagonalPushing()
8108   -----------------------------------------------------------------------------
8109   check if diagonal input device direction results in pushing of object
8110   (by checking if the alternative direction is walkable, diggable, ...)
8111   =============================================================================
8112 */
8113
8114 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8115                                     int x, int y, int real_dx, int real_dy)
8116 {
8117   int jx, jy, dx, dy, xx, yy;
8118
8119   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
8120     return TRUE;
8121
8122   /* diagonal direction: check alternative direction */
8123   jx = player->jx;
8124   jy = player->jy;
8125   dx = x - jx;
8126   dy = y - jy;
8127   xx = jx + (dx == 0 ? real_dx : 0);
8128   yy = jy + (dy == 0 ? real_dy : 0);
8129
8130   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8131 }
8132
8133 /*
8134   =============================================================================
8135   DigField()
8136   -----------------------------------------------------------------------------
8137   x, y:                 field next to player (non-diagonal) to try to dig to
8138   real_dx, real_dy:     direction as read from input device (can be diagonal)
8139   =============================================================================
8140 */
8141
8142 int DigField(struct PlayerInfo *player,
8143              int x, int y, int real_dx, int real_dy, int mode)
8144 {
8145   static int change_sides[4] =
8146   {
8147     CH_SIDE_RIGHT,      /* moving left  */
8148     CH_SIDE_LEFT,       /* moving right */
8149     CH_SIDE_BOTTOM,     /* moving up    */
8150     CH_SIDE_TOP,        /* moving down  */
8151   };
8152   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8153   int jx = player->jx, jy = player->jy;
8154   int dx = x - jx, dy = y - jy;
8155   int nextx = x + dx, nexty = y + dy;
8156   int move_direction = (dx == -1 ? MV_LEFT :
8157                         dx == +1 ? MV_RIGHT :
8158                         dy == -1 ? MV_UP :
8159                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8160   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8161   int element;
8162
8163   if (player->MovPos == 0)
8164   {
8165     player->is_digging = FALSE;
8166     player->is_collecting = FALSE;
8167   }
8168
8169   if (player->MovPos == 0)      /* last pushing move finished */
8170     player->is_pushing = FALSE;
8171
8172   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
8173   {
8174     player->is_switching = FALSE;
8175     player->push_delay = 0;
8176
8177     return MF_NO_ACTION;
8178   }
8179
8180   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8181     return MF_NO_ACTION;
8182
8183 #if 0
8184   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8185 #else
8186   if (IS_TUBE(Feld[jx][jy]) ||
8187       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8188 #endif
8189   {
8190     int i = 0;
8191     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8192     int tube_leave_directions[][2] =
8193     {
8194       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8195       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
8196       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
8197       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
8198       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
8199       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
8200       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
8201       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
8202       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
8203       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
8204       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
8205       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8206     };
8207
8208     while (tube_leave_directions[i][0] != tube_element)
8209     {
8210       i++;
8211       if (tube_leave_directions[i][0] == -1)    /* should not happen */
8212         break;
8213     }
8214
8215     if (!(tube_leave_directions[i][1] & move_direction))
8216       return MF_NO_ACTION;      /* tube has no opening in this direction */
8217   }
8218
8219   element = Feld[x][y];
8220
8221   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8222       game.engine_version >= VERSION_IDENT(2,2,0,0))
8223     return MF_NO_ACTION;
8224
8225   switch (element)
8226   {
8227     case EL_SP_PORT_LEFT:
8228     case EL_SP_PORT_RIGHT:
8229     case EL_SP_PORT_UP:
8230     case EL_SP_PORT_DOWN:
8231     case EL_SP_PORT_HORIZONTAL:
8232     case EL_SP_PORT_VERTICAL:
8233     case EL_SP_PORT_ANY:
8234     case EL_SP_GRAVITY_PORT_LEFT:
8235     case EL_SP_GRAVITY_PORT_RIGHT:
8236     case EL_SP_GRAVITY_PORT_UP:
8237     case EL_SP_GRAVITY_PORT_DOWN:
8238       if ((dx == -1 &&
8239            element != EL_SP_PORT_LEFT &&
8240            element != EL_SP_GRAVITY_PORT_LEFT &&
8241            element != EL_SP_PORT_HORIZONTAL &&
8242            element != EL_SP_PORT_ANY) ||
8243           (dx == +1 &&
8244            element != EL_SP_PORT_RIGHT &&
8245            element != EL_SP_GRAVITY_PORT_RIGHT &&
8246            element != EL_SP_PORT_HORIZONTAL &&
8247            element != EL_SP_PORT_ANY) ||
8248           (dy == -1 &&
8249            element != EL_SP_PORT_UP &&
8250            element != EL_SP_GRAVITY_PORT_UP &&
8251            element != EL_SP_PORT_VERTICAL &&
8252            element != EL_SP_PORT_ANY) ||
8253           (dy == +1 &&
8254            element != EL_SP_PORT_DOWN &&
8255            element != EL_SP_GRAVITY_PORT_DOWN &&
8256            element != EL_SP_PORT_VERTICAL &&
8257            element != EL_SP_PORT_ANY) ||
8258           !IN_LEV_FIELD(nextx, nexty) ||
8259           !IS_FREE(nextx, nexty))
8260         return MF_NO_ACTION;
8261
8262       if (element == EL_SP_GRAVITY_PORT_LEFT ||
8263           element == EL_SP_GRAVITY_PORT_RIGHT ||
8264           element == EL_SP_GRAVITY_PORT_UP ||
8265           element == EL_SP_GRAVITY_PORT_DOWN)
8266         game.gravity = !game.gravity;
8267
8268       /* automatically move to the next field with double speed */
8269       player->programmed_action = move_direction;
8270       DOUBLE_PLAYER_SPEED(player);
8271
8272       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8273       break;
8274
8275     case EL_TUBE_ANY:
8276     case EL_TUBE_VERTICAL:
8277     case EL_TUBE_HORIZONTAL:
8278     case EL_TUBE_VERTICAL_LEFT:
8279     case EL_TUBE_VERTICAL_RIGHT:
8280     case EL_TUBE_HORIZONTAL_UP:
8281     case EL_TUBE_HORIZONTAL_DOWN:
8282     case EL_TUBE_LEFT_UP:
8283     case EL_TUBE_LEFT_DOWN:
8284     case EL_TUBE_RIGHT_UP:
8285     case EL_TUBE_RIGHT_DOWN:
8286       {
8287         int i = 0;
8288         int tube_enter_directions[][2] =
8289         {
8290           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8291           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
8292           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
8293           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
8294           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
8295           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
8296           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
8297           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
8298           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
8299           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
8300           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
8301           { -1,                         MV_NO_MOVING                         }
8302         };
8303
8304         while (tube_enter_directions[i][0] != element)
8305         {
8306           i++;
8307           if (tube_enter_directions[i][0] == -1)        /* should not happen */
8308             break;
8309         }
8310
8311         if (!(tube_enter_directions[i][1] & move_direction))
8312           return MF_NO_ACTION;  /* tube has no opening in this direction */
8313
8314         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8315       }
8316       break;
8317
8318     default:
8319
8320       if (IS_WALKABLE(element))
8321       {
8322         int sound_action = ACTION_WALKING;
8323
8324         if (element >= EL_GATE_1 && element <= EL_GATE_4)
8325         {
8326           if (!player->key[element - EL_GATE_1])
8327             return MF_NO_ACTION;
8328         }
8329         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8330         {
8331           if (!player->key[element - EL_GATE_1_GRAY])
8332             return MF_NO_ACTION;
8333         }
8334         else if (element == EL_EXIT_OPEN ||
8335                  element == EL_SP_EXIT_OPEN ||
8336                  element == EL_SP_EXIT_OPENING)
8337         {
8338           sound_action = ACTION_PASSING;        /* player is passing exit */
8339         }
8340         else if (element == EL_EMPTY)
8341         {
8342           sound_action = ACTION_MOVING;         /* nothing to walk on */
8343         }
8344
8345         /* play sound from background or player, whatever is available */
8346         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8347           PlayLevelSoundElementAction(x, y, element, sound_action);
8348         else
8349           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8350
8351         break;
8352       }
8353       else if (IS_PASSABLE(element))
8354       {
8355         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8356           return MF_NO_ACTION;
8357
8358 #if 1
8359         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
8360           return MF_NO_ACTION;
8361 #endif
8362
8363         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8364         {
8365           if (!player->key[element - EL_EM_GATE_1])
8366             return MF_NO_ACTION;
8367         }
8368         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8369         {
8370           if (!player->key[element - EL_EM_GATE_1_GRAY])
8371             return MF_NO_ACTION;
8372         }
8373
8374         /* automatically move to the next field with double speed */
8375         player->programmed_action = move_direction;
8376         DOUBLE_PLAYER_SPEED(player);
8377
8378         PlayLevelSoundAction(x, y, ACTION_PASSING);
8379
8380         break;
8381       }
8382       else if (IS_DIGGABLE(element))
8383       {
8384         RemoveField(x, y);
8385
8386         if (mode != DF_SNAP)
8387         {
8388 #if 1
8389           GfxElement[x][y] = GFX_ELEMENT(element);
8390 #else
8391           GfxElement[x][y] =
8392             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8393 #endif
8394           player->is_digging = TRUE;
8395         }
8396
8397         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8398
8399         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8400
8401 #if 1
8402         if (mode == DF_SNAP)
8403           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8404 #endif
8405
8406         break;
8407       }
8408       else if (IS_COLLECTIBLE(element))
8409       {
8410         RemoveField(x, y);
8411
8412         if (mode != DF_SNAP)
8413         {
8414           GfxElement[x][y] = element;
8415           player->is_collecting = TRUE;
8416         }
8417
8418         if (element == EL_SPEED_PILL)
8419           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8420         else if (element == EL_EXTRA_TIME && level.time > 0)
8421         {
8422           TimeLeft += 10;
8423           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8424         }
8425         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8426         {
8427           player->shield_normal_time_left += 10;
8428           if (element == EL_SHIELD_DEADLY)
8429             player->shield_deadly_time_left += 10;
8430         }
8431         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8432         {
8433           if (player->inventory_size < MAX_INVENTORY_SIZE)
8434             player->inventory_element[player->inventory_size++] = element;
8435
8436           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8437                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8438         }
8439         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8440         {
8441           player->dynabomb_count++;
8442           player->dynabombs_left++;
8443         }
8444         else if (element == EL_DYNABOMB_INCREASE_SIZE)
8445         {
8446           player->dynabomb_size++;
8447         }
8448         else if (element == EL_DYNABOMB_INCREASE_POWER)
8449         {
8450           player->dynabomb_xl = TRUE;
8451         }
8452         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8453                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8454         {
8455           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8456                         element - EL_KEY_1 : element - EL_EM_KEY_1);
8457
8458           player->key[key_nr] = TRUE;
8459
8460           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8461                              el2edimg(EL_KEY_1 + key_nr));
8462           redraw_mask |= REDRAW_DOOR_1;
8463         }
8464         else if (IS_ENVELOPE(element))
8465         {
8466 #if 1
8467           player->show_envelope = element;
8468 #else
8469           ShowEnvelope(element - EL_ENVELOPE_1);
8470 #endif
8471         }
8472         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8473         {
8474           int i;
8475
8476           for (i = 0; i < element_info[element].collect_count; i++)
8477             if (player->inventory_size < MAX_INVENTORY_SIZE)
8478               player->inventory_element[player->inventory_size++] = element;
8479
8480           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8481                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8482         }
8483         else if (element_info[element].collect_count > 0)
8484         {
8485           local_player->gems_still_needed -=
8486             element_info[element].collect_count;
8487           if (local_player->gems_still_needed < 0)
8488             local_player->gems_still_needed = 0;
8489
8490           DrawText(DX_EMERALDS, DY_EMERALDS,
8491                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8492         }
8493
8494         RaiseScoreElement(element);
8495         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8496
8497         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8498
8499 #if 1
8500         if (mode == DF_SNAP)
8501           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8502 #endif
8503
8504         break;
8505       }
8506       else if (IS_PUSHABLE(element))
8507       {
8508         if (mode == DF_SNAP && element != EL_BD_ROCK)
8509           return MF_NO_ACTION;
8510
8511         if (CAN_FALL(element) && dy)
8512           return MF_NO_ACTION;
8513
8514         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8515             !(element == EL_SPRING && use_spring_bug))
8516           return MF_NO_ACTION;
8517
8518 #if 1
8519         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8520             ((move_direction & MV_VERTICAL &&
8521               ((element_info[element].move_pattern & MV_LEFT &&
8522                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8523                (element_info[element].move_pattern & MV_RIGHT &&
8524                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8525              (move_direction & MV_HORIZONTAL &&
8526               ((element_info[element].move_pattern & MV_UP &&
8527                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8528                (element_info[element].move_pattern & MV_DOWN &&
8529                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8530           return MF_NO_ACTION;
8531 #endif
8532
8533 #if 1
8534         /* do not push elements already moving away faster than player */
8535         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8536             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8537           return MF_NO_ACTION;
8538 #else
8539         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8540           return MF_NO_ACTION;
8541 #endif
8542
8543 #if 1
8544         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8545         {
8546           if (player->push_delay_value == -1)
8547             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8548         }
8549         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8550         {
8551           if (!player->is_pushing)
8552             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8553         }
8554
8555         /*
8556         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8557             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8558              !player_is_pushing))
8559           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8560         */
8561 #else
8562         if (!player->is_pushing &&
8563             game.engine_version >= VERSION_IDENT(2,2,0,7))
8564           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8565 #endif
8566
8567 #if 0
8568         printf("::: push delay: %ld [%d, %d] [%d]\n",
8569                player->push_delay_value, FrameCounter, game.engine_version,
8570                player->is_pushing);
8571 #endif
8572
8573         player->is_pushing = TRUE;
8574
8575         if (!(IN_LEV_FIELD(nextx, nexty) &&
8576               (IS_FREE(nextx, nexty) ||
8577                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8578                 IS_SB_ELEMENT(element)))))
8579           return MF_NO_ACTION;
8580
8581         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8582           return MF_NO_ACTION;
8583
8584         if (player->push_delay == 0)    /* new pushing; restart delay */
8585           player->push_delay = FrameCounter;
8586
8587         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8588             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8589             element != EL_SPRING && element != EL_BALLOON)
8590         {
8591           /* make sure that there is no move delay before next try to push */
8592           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8593             player->move_delay = INITIAL_MOVE_DELAY_OFF;
8594
8595           return MF_NO_ACTION;
8596         }
8597
8598 #if 0
8599         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8600 #endif
8601
8602         if (IS_SB_ELEMENT(element))
8603         {
8604           if (element == EL_SOKOBAN_FIELD_FULL)
8605           {
8606             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8607             local_player->sokobanfields_still_needed++;
8608           }
8609
8610           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8611           {
8612             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8613             local_player->sokobanfields_still_needed--;
8614           }
8615
8616           Feld[x][y] = EL_SOKOBAN_OBJECT;
8617
8618           if (Back[x][y] == Back[nextx][nexty])
8619             PlayLevelSoundAction(x, y, ACTION_PUSHING);
8620           else if (Back[x][y] != 0)
8621             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8622                                         ACTION_EMPTYING);
8623           else
8624             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8625                                         ACTION_FILLING);
8626
8627           if (local_player->sokobanfields_still_needed == 0 &&
8628               game.emulation == EMU_SOKOBAN)
8629           {
8630             player->LevelSolved = player->GameOver = TRUE;
8631             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8632           }
8633         }
8634         else
8635           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8636
8637         InitMovingField(x, y, move_direction);
8638         GfxAction[x][y] = ACTION_PUSHING;
8639
8640         if (mode == DF_SNAP)
8641           ContinueMoving(x, y);
8642         else
8643           MovPos[x][y] = (dx != 0 ? dx : dy);
8644
8645         Pushed[x][y] = TRUE;
8646         Pushed[nextx][nexty] = TRUE;
8647
8648         if (game.engine_version < VERSION_IDENT(2,2,0,7))
8649           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8650         else
8651           player->push_delay_value = -1;        /* get new value later */
8652
8653         CheckTriggeredElementSideChange(x, y, element, dig_side,
8654                                         CE_OTHER_GETS_PUSHED);
8655         CheckElementSideChange(x, y, element, dig_side,
8656                                CE_PUSHED_BY_PLAYER, -1);
8657
8658         break;
8659       }
8660       else if (IS_SWITCHABLE(element))
8661       {
8662         if (PLAYER_SWITCHING(player, x, y))
8663           return MF_ACTION;
8664
8665         player->is_switching = TRUE;
8666         player->switch_x = x;
8667         player->switch_y = y;
8668
8669         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8670
8671         if (element == EL_ROBOT_WHEEL)
8672         {
8673           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8674           ZX = x;
8675           ZY = y;
8676
8677           DrawLevelField(x, y);
8678         }
8679         else if (element == EL_SP_TERMINAL)
8680         {
8681           int xx, yy;
8682
8683           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8684           {
8685             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8686               Bang(xx, yy);
8687             else if (Feld[xx][yy] == EL_SP_TERMINAL)
8688               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8689           }
8690         }
8691         else if (IS_BELT_SWITCH(element))
8692         {
8693           ToggleBeltSwitch(x, y);
8694         }
8695         else if (element == EL_SWITCHGATE_SWITCH_UP ||
8696                  element == EL_SWITCHGATE_SWITCH_DOWN)
8697         {
8698           ToggleSwitchgateSwitch(x, y);
8699         }
8700         else if (element == EL_LIGHT_SWITCH ||
8701                  element == EL_LIGHT_SWITCH_ACTIVE)
8702         {
8703           ToggleLightSwitch(x, y);
8704
8705 #if 0
8706           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8707                          SND_LIGHT_SWITCH_ACTIVATING :
8708                          SND_LIGHT_SWITCH_DEACTIVATING);
8709 #endif
8710         }
8711         else if (element == EL_TIMEGATE_SWITCH)
8712         {
8713           ActivateTimegateSwitch(x, y);
8714         }
8715         else if (element == EL_BALLOON_SWITCH_LEFT ||
8716                  element == EL_BALLOON_SWITCH_RIGHT ||
8717                  element == EL_BALLOON_SWITCH_UP ||
8718                  element == EL_BALLOON_SWITCH_DOWN ||
8719                  element == EL_BALLOON_SWITCH_ANY)
8720         {
8721           if (element == EL_BALLOON_SWITCH_ANY)
8722             game.balloon_dir = move_direction;
8723           else
8724             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
8725                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8726                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
8727                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
8728                                 MV_NO_MOVING);
8729         }
8730         else if (element == EL_LAMP)
8731         {
8732           Feld[x][y] = EL_LAMP_ACTIVE;
8733           local_player->lights_still_needed--;
8734
8735           DrawLevelField(x, y);
8736         }
8737         else if (element == EL_TIME_ORB_FULL)
8738         {
8739           Feld[x][y] = EL_TIME_ORB_EMPTY;
8740           TimeLeft += 10;
8741           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8742
8743           DrawLevelField(x, y);
8744
8745 #if 0
8746           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8747 #endif
8748         }
8749
8750         return MF_ACTION;
8751       }
8752       else
8753       {
8754         if (!PLAYER_SWITCHING(player, x, y))
8755         {
8756           player->is_switching = TRUE;
8757           player->switch_x = x;
8758           player->switch_y = y;
8759
8760           CheckTriggeredElementSideChange(x, y, element, dig_side,
8761                                           CE_OTHER_IS_SWITCHING);
8762           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8763         }
8764
8765         CheckTriggeredElementSideChange(x, y, element, dig_side,
8766                                         CE_OTHER_GETS_PRESSED);
8767         CheckElementSideChange(x, y, element, dig_side,
8768                                CE_PRESSED_BY_PLAYER, -1);
8769       }
8770
8771       return MF_NO_ACTION;
8772   }
8773
8774   player->push_delay = 0;
8775
8776   if (Feld[x][y] != element)            /* really digged/collected something */
8777     player->is_collecting = !player->is_digging;
8778
8779   return MF_MOVING;
8780 }
8781
8782 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8783 {
8784   int jx = player->jx, jy = player->jy;
8785   int x = jx + dx, y = jy + dy;
8786   int snap_direction = (dx == -1 ? MV_LEFT :
8787                         dx == +1 ? MV_RIGHT :
8788                         dy == -1 ? MV_UP :
8789                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8790
8791   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8792     return FALSE;
8793
8794   if (!player->active || !IN_LEV_FIELD(x, y))
8795     return FALSE;
8796
8797   if (dx && dy)
8798     return FALSE;
8799
8800   if (!dx && !dy)
8801   {
8802     if (player->MovPos == 0)
8803       player->is_pushing = FALSE;
8804
8805     player->is_snapping = FALSE;
8806
8807     if (player->MovPos == 0)
8808     {
8809       player->is_moving = FALSE;
8810       player->is_digging = FALSE;
8811       player->is_collecting = FALSE;
8812     }
8813
8814     return FALSE;
8815   }
8816
8817   if (player->is_snapping)
8818     return FALSE;
8819
8820   player->MovDir = snap_direction;
8821
8822   player->is_moving = FALSE;
8823   player->is_digging = FALSE;
8824   player->is_collecting = FALSE;
8825
8826   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8827     return FALSE;
8828
8829   player->is_snapping = TRUE;
8830
8831   player->is_moving = FALSE;
8832   player->is_digging = FALSE;
8833   player->is_collecting = FALSE;
8834
8835   DrawLevelField(x, y);
8836   BackToFront();
8837
8838   return TRUE;
8839 }
8840
8841 boolean DropElement(struct PlayerInfo *player)
8842 {
8843   int jx = player->jx, jy = player->jy;
8844   int old_element;
8845
8846   if (!player->active || player->MovPos)
8847     return FALSE;
8848
8849   old_element = Feld[jx][jy];
8850
8851   /* check if player has anything that can be dropped */
8852   if (player->inventory_size == 0 && player->dynabombs_left == 0)
8853     return FALSE;
8854
8855   /* check if anything can be dropped at the current position */
8856   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8857     return FALSE;
8858
8859   /* collected custom elements can only be dropped on empty fields */
8860   if (player->inventory_size > 0 &&
8861       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8862       && old_element != EL_EMPTY)
8863     return FALSE;
8864
8865   if (old_element != EL_EMPTY)
8866     Back[jx][jy] = old_element;         /* store old element on this field */
8867
8868   MovDelay[jx][jy] = 96;
8869
8870   ResetGfxAnimation(jx, jy);
8871   ResetRandomAnimationValue(jx, jy);
8872
8873   if (player->inventory_size > 0)
8874   {
8875     int new_element = player->inventory_element[--player->inventory_size];
8876
8877     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8878                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8879                     new_element);
8880
8881     DrawText(DX_DYNAMITE, DY_DYNAMITE,
8882              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8883
8884     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8885       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8886
8887     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8888
8889     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8890     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8891
8892     TestIfElementTouchesCustomElement(jx, jy);
8893   }
8894   else          /* player is dropping a dyna bomb */
8895   {
8896     player->dynabombs_left--;
8897
8898     Feld[jx][jy] =
8899       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8900
8901     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8902       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8903
8904     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8905   }
8906
8907   return TRUE;
8908 }
8909
8910 /* ------------------------------------------------------------------------- */
8911 /* game sound playing functions                                              */
8912 /* ------------------------------------------------------------------------- */
8913
8914 static int *loop_sound_frame = NULL;
8915 static int *loop_sound_volume = NULL;
8916
8917 void InitPlayLevelSound()
8918 {
8919   int num_sounds = getSoundListSize();
8920
8921   if (loop_sound_frame != NULL)
8922     free(loop_sound_frame);
8923
8924   if (loop_sound_volume != NULL)
8925     free(loop_sound_volume);
8926
8927   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8928   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8929 }
8930
8931 static void PlayLevelSound(int x, int y, int nr)
8932 {
8933   int sx = SCREENX(x), sy = SCREENY(y);
8934   int volume, stereo_position;
8935   int max_distance = 8;
8936   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8937
8938   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8939       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8940     return;
8941
8942   if (!IN_LEV_FIELD(x, y) ||
8943       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8944       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8945     return;
8946
8947   volume = SOUND_MAX_VOLUME;
8948
8949   if (!IN_SCR_FIELD(sx, sy))
8950   {
8951     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8952     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8953
8954     volume -= volume * (dx > dy ? dx : dy) / max_distance;
8955   }
8956
8957   stereo_position = (SOUND_MAX_LEFT +
8958                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8959                      (SCR_FIELDX + 2 * max_distance));
8960
8961   if (IS_LOOP_SOUND(nr))
8962   {
8963     /* This assures that quieter loop sounds do not overwrite louder ones,
8964        while restarting sound volume comparison with each new game frame. */
8965
8966     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8967       return;
8968
8969     loop_sound_volume[nr] = volume;
8970     loop_sound_frame[nr] = FrameCounter;
8971   }
8972
8973   PlaySoundExt(nr, volume, stereo_position, type);
8974 }
8975
8976 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8977 {
8978   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8979                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
8980                  y < LEVELY(BY1) ? LEVELY(BY1) :
8981                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
8982                  sound_action);
8983 }
8984
8985 static void PlayLevelSoundAction(int x, int y, int action)
8986 {
8987   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8988 }
8989
8990 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8991 {
8992   int sound_effect = element_info[element].sound[action];
8993
8994   if (sound_effect != SND_UNDEFINED)
8995     PlayLevelSound(x, y, sound_effect);
8996 }
8997
8998 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
8999                                               int action)
9000 {
9001   int sound_effect = element_info[element].sound[action];
9002
9003   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9004     PlayLevelSound(x, y, sound_effect);
9005 }
9006
9007 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9008 {
9009   int sound_effect = element_info[Feld[x][y]].sound[action];
9010
9011   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9012     PlayLevelSound(x, y, sound_effect);
9013 }
9014
9015 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9016 {
9017   int sound_effect = element_info[Feld[x][y]].sound[action];
9018
9019   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9020     StopSound(sound_effect);
9021 }
9022
9023 static void PlayLevelMusic()
9024 {
9025   if (levelset.music[level_nr] != MUS_UNDEFINED)
9026     PlayMusic(levelset.music[level_nr]);        /* from config file */
9027   else
9028     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
9029 }
9030
9031 void RaiseScore(int value)
9032 {
9033   local_player->score += value;
9034   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9035 }
9036
9037 void RaiseScoreElement(int element)
9038 {
9039   switch(element)
9040   {
9041     case EL_EMERALD:
9042     case EL_BD_DIAMOND:
9043     case EL_EMERALD_YELLOW:
9044     case EL_EMERALD_RED:
9045     case EL_EMERALD_PURPLE:
9046     case EL_SP_INFOTRON:
9047       RaiseScore(level.score[SC_EMERALD]);
9048       break;
9049     case EL_DIAMOND:
9050       RaiseScore(level.score[SC_DIAMOND]);
9051       break;
9052     case EL_CRYSTAL:
9053       RaiseScore(level.score[SC_CRYSTAL]);
9054       break;
9055     case EL_PEARL:
9056       RaiseScore(level.score[SC_PEARL]);
9057       break;
9058     case EL_BUG:
9059     case EL_BD_BUTTERFLY:
9060     case EL_SP_ELECTRON:
9061       RaiseScore(level.score[SC_BUG]);
9062       break;
9063     case EL_SPACESHIP:
9064     case EL_BD_FIREFLY:
9065     case EL_SP_SNIKSNAK:
9066       RaiseScore(level.score[SC_SPACESHIP]);
9067       break;
9068     case EL_YAMYAM:
9069     case EL_DARK_YAMYAM:
9070       RaiseScore(level.score[SC_YAMYAM]);
9071       break;
9072     case EL_ROBOT:
9073       RaiseScore(level.score[SC_ROBOT]);
9074       break;
9075     case EL_PACMAN:
9076       RaiseScore(level.score[SC_PACMAN]);
9077       break;
9078     case EL_NUT:
9079       RaiseScore(level.score[SC_NUT]);
9080       break;
9081     case EL_DYNAMITE:
9082     case EL_SP_DISK_RED:
9083     case EL_DYNABOMB_INCREASE_NUMBER:
9084     case EL_DYNABOMB_INCREASE_SIZE:
9085     case EL_DYNABOMB_INCREASE_POWER:
9086       RaiseScore(level.score[SC_DYNAMITE]);
9087       break;
9088     case EL_SHIELD_NORMAL:
9089     case EL_SHIELD_DEADLY:
9090       RaiseScore(level.score[SC_SHIELD]);
9091       break;
9092     case EL_EXTRA_TIME:
9093       RaiseScore(level.score[SC_TIME_BONUS]);
9094       break;
9095     case EL_KEY_1:
9096     case EL_KEY_2:
9097     case EL_KEY_3:
9098     case EL_KEY_4:
9099       RaiseScore(level.score[SC_KEY]);
9100       break;
9101     default:
9102       RaiseScore(element_info[element].collect_score);
9103       break;
9104   }
9105 }
9106
9107 void RequestQuitGame(boolean ask_if_really_quit)
9108 {
9109   if (AllPlayersGone ||
9110       !ask_if_really_quit ||
9111       level_editor_test_game ||
9112       Request("Do you really want to quit the game ?",
9113               REQ_ASK | REQ_STAY_CLOSED))
9114   {
9115 #if defined(PLATFORM_UNIX)
9116     if (options.network)
9117       SendToServer_StopPlaying();
9118     else
9119 #endif
9120     {
9121       game_status = GAME_MODE_MAIN;
9122       DrawMainMenu();
9123     }
9124   }
9125   else
9126   {
9127     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9128   }
9129 }
9130
9131
9132 /* ---------- new game button stuff ---------------------------------------- */
9133
9134 /* graphic position values for game buttons */
9135 #define GAME_BUTTON_XSIZE       30
9136 #define GAME_BUTTON_YSIZE       30
9137 #define GAME_BUTTON_XPOS        5
9138 #define GAME_BUTTON_YPOS        215
9139 #define SOUND_BUTTON_XPOS       5
9140 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9141
9142 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9143 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9144 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9145 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9146 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9147 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9148
9149 static struct
9150 {
9151   int x, y;
9152   int gadget_id;
9153   char *infotext;
9154 } gamebutton_info[NUM_GAME_BUTTONS] =
9155 {
9156   {
9157     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
9158     GAME_CTRL_ID_STOP,
9159     "stop game"
9160   },
9161   {
9162     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
9163     GAME_CTRL_ID_PAUSE,
9164     "pause game"
9165   },
9166   {
9167     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
9168     GAME_CTRL_ID_PLAY,
9169     "play game"
9170   },
9171   {
9172     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
9173     SOUND_CTRL_ID_MUSIC,
9174     "background music on/off"
9175   },
9176   {
9177     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
9178     SOUND_CTRL_ID_LOOPS,
9179     "sound loops on/off"
9180   },
9181   {
9182     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
9183     SOUND_CTRL_ID_SIMPLE,
9184     "normal sounds on/off"
9185   }
9186 };
9187
9188 void CreateGameButtons()
9189 {
9190   int i;
9191
9192   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9193   {
9194     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9195     struct GadgetInfo *gi;
9196     int button_type;
9197     boolean checked;
9198     unsigned long event_mask;
9199     int gd_xoffset, gd_yoffset;
9200     int gd_x1, gd_x2, gd_y1, gd_y2;
9201     int id = i;
9202
9203     gd_xoffset = gamebutton_info[i].x;
9204     gd_yoffset = gamebutton_info[i].y;
9205     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9206     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9207
9208     if (id == GAME_CTRL_ID_STOP ||
9209         id == GAME_CTRL_ID_PAUSE ||
9210         id == GAME_CTRL_ID_PLAY)
9211     {
9212       button_type = GD_TYPE_NORMAL_BUTTON;
9213       checked = FALSE;
9214       event_mask = GD_EVENT_RELEASED;
9215       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9216       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9217     }
9218     else
9219     {
9220       button_type = GD_TYPE_CHECK_BUTTON;
9221       checked =
9222         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
9223          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
9224          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
9225       event_mask = GD_EVENT_PRESSED;
9226       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
9227       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
9228     }
9229
9230     gi = CreateGadget(GDI_CUSTOM_ID, id,
9231                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
9232                       GDI_X, DX + gd_xoffset,
9233                       GDI_Y, DY + gd_yoffset,
9234                       GDI_WIDTH, GAME_BUTTON_XSIZE,
9235                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
9236                       GDI_TYPE, button_type,
9237                       GDI_STATE, GD_BUTTON_UNPRESSED,
9238                       GDI_CHECKED, checked,
9239                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
9240                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
9241                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
9242                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
9243                       GDI_EVENT_MASK, event_mask,
9244                       GDI_CALLBACK_ACTION, HandleGameButtons,
9245                       GDI_END);
9246
9247     if (gi == NULL)
9248       Error(ERR_EXIT, "cannot create gadget");
9249
9250     game_gadget[id] = gi;
9251   }
9252 }
9253
9254 void FreeGameButtons()
9255 {
9256   int i;
9257
9258   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9259     FreeGadget(game_gadget[i]);
9260 }
9261
9262 static void MapGameButtons()
9263 {
9264   int i;
9265
9266   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9267     MapGadget(game_gadget[i]);
9268 }
9269
9270 void UnmapGameButtons()
9271 {
9272   int i;
9273
9274   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9275     UnmapGadget(game_gadget[i]);
9276 }
9277
9278 static void HandleGameButtons(struct GadgetInfo *gi)
9279 {
9280   int id = gi->custom_id;
9281
9282   if (game_status != GAME_MODE_PLAYING)
9283     return;
9284
9285   switch (id)
9286   {
9287     case GAME_CTRL_ID_STOP:
9288       RequestQuitGame(TRUE);
9289       break;
9290
9291     case GAME_CTRL_ID_PAUSE:
9292       if (options.network)
9293       {
9294 #if defined(PLATFORM_UNIX)
9295         if (tape.pausing)
9296           SendToServer_ContinuePlaying();
9297         else
9298           SendToServer_PausePlaying();
9299 #endif
9300       }
9301       else
9302         TapeTogglePause(TAPE_TOGGLE_MANUAL);
9303       break;
9304
9305     case GAME_CTRL_ID_PLAY:
9306       if (tape.pausing)
9307       {
9308 #if defined(PLATFORM_UNIX)
9309         if (options.network)
9310           SendToServer_ContinuePlaying();
9311         else
9312 #endif
9313         {
9314           tape.pausing = FALSE;
9315           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9316         }
9317       }
9318       break;
9319
9320     case SOUND_CTRL_ID_MUSIC:
9321       if (setup.sound_music)
9322       { 
9323         setup.sound_music = FALSE;
9324         FadeMusic();
9325       }
9326       else if (audio.music_available)
9327       { 
9328         setup.sound = setup.sound_music = TRUE;
9329
9330         SetAudioMode(setup.sound);
9331
9332         PlayLevelMusic();
9333       }
9334       break;
9335
9336     case SOUND_CTRL_ID_LOOPS:
9337       if (setup.sound_loops)
9338         setup.sound_loops = FALSE;
9339       else if (audio.loops_available)
9340       {
9341         setup.sound = setup.sound_loops = TRUE;
9342         SetAudioMode(setup.sound);
9343       }
9344       break;
9345
9346     case SOUND_CTRL_ID_SIMPLE:
9347       if (setup.sound_simple)
9348         setup.sound_simple = FALSE;
9349       else if (audio.sound_available)
9350       {
9351         setup.sound = setup.sound_simple = TRUE;
9352         SetAudioMode(setup.sound);
9353       }
9354       break;
9355
9356     default:
9357       break;
9358   }
9359 }