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