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