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