f5558fdb57e8ea0a87f7975c8650490102cce5bb
[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_8; 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_3; 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   boolean was_waiting = player->is_waiting;
5996
5997   if (is_waiting)
5998   {
5999     int jx = player->jx, jy = player->jy;
6000     int element = player->element_nr;
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     player->is_waiting = FALSE;
6041     player->is_bored = FALSE;
6042     player->is_sleeping = FALSE;
6043
6044     player->frame_counter_bored = -1;
6045     player->frame_counter_sleeping = -1;
6046
6047     player->anim_delay_counter = 0;
6048     player->post_delay_counter = 0;
6049   }
6050 }
6051
6052 #if 1
6053 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6054 {
6055 #if 0
6056   static byte stored_player_action[MAX_PLAYERS];
6057   static int num_stored_actions = 0;
6058 #endif
6059   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6060   int left      = player_action & JOY_LEFT;
6061   int right     = player_action & JOY_RIGHT;
6062   int up        = player_action & JOY_UP;
6063   int down      = player_action & JOY_DOWN;
6064   int button1   = player_action & JOY_BUTTON_1;
6065   int button2   = player_action & JOY_BUTTON_2;
6066   int dx        = (left ? -1    : right ? 1     : 0);
6067   int dy        = (up   ? -1    : down  ? 1     : 0);
6068
6069 #if 0
6070   stored_player_action[player->index_nr] = 0;
6071   num_stored_actions++;
6072 #endif
6073
6074 #if 0
6075   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6076 #endif
6077
6078   if (!player->active || tape.pausing)
6079     return 0;
6080
6081   if (player_action)
6082   {
6083 #if 0
6084     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6085 #endif
6086
6087     if (button1)
6088       snapped = SnapField(player, dx, dy);
6089     else
6090     {
6091       if (button2)
6092         dropped = DropElement(player);
6093
6094       moved = MovePlayer(player, dx, dy);
6095     }
6096
6097     if (tape.single_step && tape.recording && !tape.pausing)
6098     {
6099       if (button1 || (dropped && !moved))
6100       {
6101         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6102         SnapField(player, 0, 0);                /* stop snapping */
6103       }
6104     }
6105
6106     SetPlayerWaiting(player, FALSE);
6107
6108 #if 1
6109     return player_action;
6110 #else
6111     stored_player_action[player->index_nr] = player_action;
6112 #endif
6113   }
6114   else
6115   {
6116 #if 0
6117     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6118 #endif
6119
6120     /* no actions for this player (no input at player's configured device) */
6121
6122     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6123     SnapField(player, 0, 0);
6124     CheckGravityMovement(player);
6125
6126     if (player->MovPos == 0)
6127       SetPlayerWaiting(player, TRUE);
6128
6129     if (player->MovPos == 0)    /* needed for tape.playing */
6130       player->is_moving = FALSE;
6131
6132     return 0;
6133   }
6134
6135 #if 0
6136   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6137   {
6138     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6139
6140     TapeRecordAction(stored_player_action);
6141     num_stored_actions = 0;
6142   }
6143 #endif
6144 }
6145
6146 #else
6147
6148 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6149 {
6150   static byte stored_player_action[MAX_PLAYERS];
6151   static int num_stored_actions = 0;
6152   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6153   int left      = player_action & JOY_LEFT;
6154   int right     = player_action & JOY_RIGHT;
6155   int up        = player_action & JOY_UP;
6156   int down      = player_action & JOY_DOWN;
6157   int button1   = player_action & JOY_BUTTON_1;
6158   int button2   = player_action & JOY_BUTTON_2;
6159   int dx        = (left ? -1    : right ? 1     : 0);
6160   int dy        = (up   ? -1    : down  ? 1     : 0);
6161
6162   stored_player_action[player->index_nr] = 0;
6163   num_stored_actions++;
6164
6165   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6166
6167   if (!player->active || tape.pausing)
6168     return;
6169
6170   if (player_action)
6171   {
6172     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6173
6174     if (button1)
6175       snapped = SnapField(player, dx, dy);
6176     else
6177     {
6178       if (button2)
6179         dropped = DropElement(player);
6180
6181       moved = MovePlayer(player, dx, dy);
6182     }
6183
6184     if (tape.single_step && tape.recording && !tape.pausing)
6185     {
6186       if (button1 || (dropped && !moved))
6187       {
6188         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6189         SnapField(player, 0, 0);                /* stop snapping */
6190       }
6191     }
6192
6193     stored_player_action[player->index_nr] = player_action;
6194   }
6195   else
6196   {
6197     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6198
6199     /* no actions for this player (no input at player's configured device) */
6200
6201     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6202     SnapField(player, 0, 0);
6203     CheckGravityMovement(player);
6204
6205     if (player->MovPos == 0)
6206       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6207
6208     if (player->MovPos == 0)    /* needed for tape.playing */
6209       player->is_moving = FALSE;
6210   }
6211
6212   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6213   {
6214     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6215
6216     TapeRecordAction(stored_player_action);
6217     num_stored_actions = 0;
6218   }
6219 }
6220 #endif
6221
6222 void GameActions()
6223 {
6224   static unsigned long action_delay = 0;
6225   unsigned long action_delay_value;
6226   int magic_wall_x = 0, magic_wall_y = 0;
6227   int i, x, y, element, graphic;
6228   byte *recorded_player_action;
6229   byte summarized_player_action = 0;
6230 #if 1
6231   byte tape_action[MAX_PLAYERS];
6232 #endif
6233
6234   if (game_status != GAME_MODE_PLAYING)
6235     return;
6236
6237   action_delay_value =
6238     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6239
6240   if (tape.playing && tape.index_search && !tape.pausing)
6241     action_delay_value = 0;
6242
6243   /* ---------- main game synchronization point ---------- */
6244
6245   WaitUntilDelayReached(&action_delay, action_delay_value);
6246
6247   if (network_playing && !network_player_action_received)
6248   {
6249     /*
6250 #ifdef DEBUG
6251     printf("DEBUG: try to get network player actions in time\n");
6252 #endif
6253     */
6254
6255 #if defined(PLATFORM_UNIX)
6256     /* last chance to get network player actions without main loop delay */
6257     HandleNetworking();
6258 #endif
6259
6260     if (game_status != GAME_MODE_PLAYING)
6261       return;
6262
6263     if (!network_player_action_received)
6264     {
6265       /*
6266 #ifdef DEBUG
6267       printf("DEBUG: failed to get network player actions in time\n");
6268 #endif
6269       */
6270       return;
6271     }
6272   }
6273
6274   if (tape.pausing)
6275     return;
6276
6277 #if 0
6278   printf("::: getting new tape action [%d]\n", FrameCounter);
6279 #endif
6280
6281   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6282
6283   for (i=0; i<MAX_PLAYERS; i++)
6284   {
6285     summarized_player_action |= stored_player[i].action;
6286
6287     if (!network_playing)
6288       stored_player[i].effective_action = stored_player[i].action;
6289   }
6290
6291 #if defined(PLATFORM_UNIX)
6292   if (network_playing)
6293     SendToServer_MovePlayer(summarized_player_action);
6294 #endif
6295
6296   if (!options.network && !setup.team_mode)
6297     local_player->effective_action = summarized_player_action;
6298
6299   for (i=0; i < MAX_PLAYERS; i++)
6300   {
6301     int actual_player_action = stored_player[i].effective_action;
6302
6303     if (stored_player[i].programmed_action)
6304       actual_player_action = stored_player[i].programmed_action;
6305
6306     if (recorded_player_action)
6307       actual_player_action = recorded_player_action[i];
6308
6309     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6310
6311     if (tape.recording && tape_action[i] && !tape.player_participates[i])
6312       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
6313
6314     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6315   }
6316
6317 #if 1
6318   if (tape.recording)
6319     TapeRecordAction(tape_action);
6320 #endif
6321
6322   network_player_action_received = FALSE;
6323
6324   ScrollScreen(NULL, SCROLL_GO_ON);
6325
6326 #if 0
6327   FrameCounter++;
6328   TimeFrames++;
6329
6330   for (i=0; i<MAX_PLAYERS; i++)
6331     stored_player[i].Frame++;
6332 #endif
6333
6334 #if 1
6335   if (game.engine_version < VERSION_IDENT(2,2,0,7))
6336   {
6337     for (i=0; i<MAX_PLAYERS; i++)
6338     {
6339       struct PlayerInfo *player = &stored_player[i];
6340       int x = player->jx;
6341       int y = player->jy;
6342
6343       if (player->active && player->is_pushing && player->is_moving &&
6344           IS_MOVING(x, y))
6345       {
6346         ContinueMoving(x, y);
6347
6348         /* continue moving after pushing (this is actually a bug) */
6349         if (!IS_MOVING(x, y))
6350         {
6351           Stop[x][y] = FALSE;
6352         }
6353       }
6354     }
6355   }
6356 #endif
6357
6358   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6359   {
6360     Changed[x][y] = CE_BITMASK_DEFAULT;
6361     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6362
6363 #if DEBUG
6364     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6365     {
6366       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6367       printf("GameActions(): This should never happen!\n");
6368
6369       ChangePage[x][y] = -1;
6370     }
6371 #endif
6372
6373     Stop[x][y] = FALSE;
6374     if (WasJustMoving[x][y] > 0)
6375       WasJustMoving[x][y]--;
6376     if (WasJustFalling[x][y] > 0)
6377       WasJustFalling[x][y]--;
6378
6379     GfxFrame[x][y]++;
6380
6381 #if 1
6382     /* reset finished pushing action (not done in ContinueMoving() to allow
6383        continous pushing animation for elements with zero push delay) */
6384     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6385     {
6386       ResetGfxAnimation(x, y);
6387       DrawLevelField(x, y);
6388     }
6389 #endif
6390
6391 #if DEBUG
6392     if (IS_BLOCKED(x, y))
6393     {
6394       int oldx, oldy;
6395
6396       Blocked2Moving(x, y, &oldx, &oldy);
6397       if (!IS_MOVING(oldx, oldy))
6398       {
6399         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6400         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6401         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6402         printf("GameActions(): This should never happen!\n");
6403       }
6404     }
6405 #endif
6406   }
6407
6408   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6409   {
6410     element = Feld[x][y];
6411 #if 1
6412     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6413 #else
6414     graphic = el2img(element);
6415 #endif
6416
6417 #if 0
6418     if (element == -1)
6419     {
6420       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6421
6422       element = graphic = 0;
6423     }
6424 #endif
6425
6426     if (graphic_info[graphic].anim_global_sync)
6427       GfxFrame[x][y] = FrameCounter;
6428
6429     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6430         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6431       ResetRandomAnimationValue(x, y);
6432
6433     SetRandomAnimationValue(x, y);
6434
6435 #if 1
6436     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
6437 #endif
6438
6439     if (IS_INACTIVE(element))
6440     {
6441       if (IS_ANIMATED(graphic))
6442         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6443
6444       continue;
6445     }
6446
6447 #if 1
6448     /* this may take place after moving, so 'element' may have changed */
6449 #if 0
6450     if (IS_CHANGING(x, y))
6451 #else
6452     if (IS_CHANGING(x, y) &&
6453         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6454 #endif
6455     {
6456 #if 0
6457       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6458                     element_info[element].event_page_nr[CE_DELAY]);
6459 #else
6460       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6461 #endif
6462
6463       element = Feld[x][y];
6464       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6465     }
6466 #endif
6467
6468     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6469     {
6470       StartMoving(x, y);
6471
6472 #if 1
6473       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6474 #if 0
6475       if (element == EL_MOLE)
6476         printf("::: %d, %d, %d [%d]\n",
6477                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6478                GfxAction[x][y]);
6479 #endif
6480 #if 0
6481       if (element == EL_YAMYAM)
6482         printf("::: %d, %d, %d\n",
6483                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6484 #endif
6485 #endif
6486
6487       if (IS_ANIMATED(graphic) &&
6488           !IS_MOVING(x, y) &&
6489           !Stop[x][y])
6490       {
6491         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6492
6493 #if 0
6494         if (element == EL_BUG)
6495           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6496 #endif
6497
6498 #if 0
6499         if (element == EL_MOLE)
6500           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6501 #endif
6502       }
6503
6504       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6505         EdelsteinFunkeln(x, y);
6506     }
6507     else if ((element == EL_ACID ||
6508               element == EL_EXIT_OPEN ||
6509               element == EL_SP_EXIT_OPEN ||
6510               element == EL_SP_TERMINAL ||
6511               element == EL_SP_TERMINAL_ACTIVE ||
6512               element == EL_EXTRA_TIME ||
6513               element == EL_SHIELD_NORMAL ||
6514               element == EL_SHIELD_DEADLY) &&
6515              IS_ANIMATED(graphic))
6516       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6517     else if (IS_MOVING(x, y))
6518       ContinueMoving(x, y);
6519     else if (IS_ACTIVE_BOMB(element))
6520       CheckDynamite(x, y);
6521 #if 0
6522     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6523       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6524 #endif
6525     else if (element == EL_AMOEBA_GROWING)
6526       AmoebeWaechst(x, y);
6527     else if (element == EL_AMOEBA_SHRINKING)
6528       AmoebaDisappearing(x, y);
6529
6530 #if !USE_NEW_AMOEBA_CODE
6531     else if (IS_AMOEBALIVE(element))
6532       AmoebeAbleger(x, y);
6533 #endif
6534
6535     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6536       Life(x, y);
6537     else if (element == EL_EXIT_CLOSED)
6538       CheckExit(x, y);
6539     else if (element == EL_SP_EXIT_CLOSED)
6540       CheckExitSP(x, y);
6541     else if (element == EL_EXPANDABLE_WALL_GROWING)
6542       MauerWaechst(x, y);
6543     else if (element == EL_EXPANDABLE_WALL ||
6544              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6545              element == EL_EXPANDABLE_WALL_VERTICAL ||
6546              element == EL_EXPANDABLE_WALL_ANY)
6547       MauerAbleger(x, y);
6548     else if (element == EL_FLAMES)
6549       CheckForDragon(x, y);
6550 #if 0
6551     else if (IS_AUTO_CHANGING(element))
6552       ChangeElement(x, y);
6553 #endif
6554     else if (element == EL_EXPLOSION)
6555       ; /* drawing of correct explosion animation is handled separately */
6556     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6558
6559 #if 0
6560     /* this may take place after moving, so 'element' may have changed */
6561     if (IS_AUTO_CHANGING(Feld[x][y]))
6562       ChangeElement(x, y);
6563 #endif
6564
6565     if (IS_BELT_ACTIVE(element))
6566       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
6567
6568     if (game.magic_wall_active)
6569     {
6570       int jx = local_player->jx, jy = local_player->jy;
6571
6572       /* play the element sound at the position nearest to the player */
6573       if ((element == EL_MAGIC_WALL_FULL ||
6574            element == EL_MAGIC_WALL_ACTIVE ||
6575            element == EL_MAGIC_WALL_EMPTYING ||
6576            element == EL_BD_MAGIC_WALL_FULL ||
6577            element == EL_BD_MAGIC_WALL_ACTIVE ||
6578            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6579           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6580       {
6581         magic_wall_x = x;
6582         magic_wall_y = y;
6583       }
6584     }
6585   }
6586
6587 #if USE_NEW_AMOEBA_CODE
6588   /* new experimental amoeba growth stuff */
6589 #if 1
6590   if (!(FrameCounter % 8))
6591 #endif
6592   {
6593     static unsigned long random = 1684108901;
6594
6595     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6596     {
6597 #if 0
6598       x = (random >> 10) % lev_fieldx;
6599       y = (random >> 20) % lev_fieldy;
6600 #else
6601       x = RND(lev_fieldx);
6602       y = RND(lev_fieldy);
6603 #endif
6604       element = Feld[x][y];
6605
6606       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6607       if (!IS_PLAYER(x,y) &&
6608           (element == EL_EMPTY ||
6609            element == EL_SAND ||
6610            element == EL_QUICKSAND_EMPTY ||
6611            element == EL_ACID_SPLASH_LEFT ||
6612            element == EL_ACID_SPLASH_RIGHT))
6613       {
6614         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6615             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6616             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6617             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6618           Feld[x][y] = EL_AMOEBA_DROP;
6619       }
6620
6621       random = random * 129 + 1;
6622     }
6623   }
6624 #endif
6625
6626 #if 0
6627   if (game.explosions_delayed)
6628 #endif
6629   {
6630     game.explosions_delayed = FALSE;
6631
6632     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6633     {
6634       element = Feld[x][y];
6635
6636       if (ExplodeField[x][y])
6637         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6638       else if (element == EL_EXPLOSION)
6639         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6640
6641       ExplodeField[x][y] = EX_NO_EXPLOSION;
6642     }
6643
6644     game.explosions_delayed = TRUE;
6645   }
6646
6647   if (game.magic_wall_active)
6648   {
6649     if (!(game.magic_wall_time_left % 4))
6650     {
6651       int element = Feld[magic_wall_x][magic_wall_y];
6652
6653       if (element == EL_BD_MAGIC_WALL_FULL ||
6654           element == EL_BD_MAGIC_WALL_ACTIVE ||
6655           element == EL_BD_MAGIC_WALL_EMPTYING)
6656         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6657       else
6658         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6659     }
6660
6661     if (game.magic_wall_time_left > 0)
6662     {
6663       game.magic_wall_time_left--;
6664       if (!game.magic_wall_time_left)
6665       {
6666         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6667         {
6668           element = Feld[x][y];
6669
6670           if (element == EL_MAGIC_WALL_ACTIVE ||
6671               element == EL_MAGIC_WALL_FULL)
6672           {
6673             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6674             DrawLevelField(x, y);
6675           }
6676           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6677                    element == EL_BD_MAGIC_WALL_FULL)
6678           {
6679             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6680             DrawLevelField(x, y);
6681           }
6682         }
6683
6684         game.magic_wall_active = FALSE;
6685       }
6686     }
6687   }
6688
6689   if (game.light_time_left > 0)
6690   {
6691     game.light_time_left--;
6692
6693     if (game.light_time_left == 0)
6694       RedrawAllLightSwitchesAndInvisibleElements();
6695   }
6696
6697   if (game.timegate_time_left > 0)
6698   {
6699     game.timegate_time_left--;
6700
6701     if (game.timegate_time_left == 0)
6702       CloseAllOpenTimegates();
6703   }
6704
6705   for (i=0; i<MAX_PLAYERS; i++)
6706   {
6707     struct PlayerInfo *player = &stored_player[i];
6708
6709     if (SHIELD_ON(player))
6710     {
6711       if (player->shield_deadly_time_left)
6712         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6713       else if (player->shield_normal_time_left)
6714         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6715     }
6716   }
6717
6718   if (TimeFrames >= FRAMES_PER_SECOND)
6719   {
6720     TimeFrames = 0;
6721     TimePlayed++;
6722
6723     for (i=0; i<MAX_PLAYERS; i++)
6724     {
6725       struct PlayerInfo *player = &stored_player[i];
6726
6727       if (SHIELD_ON(player))
6728       {
6729         player->shield_normal_time_left--;
6730
6731         if (player->shield_deadly_time_left > 0)
6732           player->shield_deadly_time_left--;
6733       }
6734     }
6735
6736     if (tape.recording || tape.playing)
6737       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6738
6739     if (TimeLeft > 0)
6740     {
6741       TimeLeft--;
6742
6743       if (TimeLeft <= 10 && setup.time_limit)
6744         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6745
6746       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6747
6748       if (!TimeLeft && setup.time_limit)
6749         for (i=0; i<MAX_PLAYERS; i++)
6750           KillHero(&stored_player[i]);
6751     }
6752     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6753       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6754   }
6755
6756   DrawAllPlayers();
6757
6758   if (options.debug)                    /* calculate frames per second */
6759   {
6760     static unsigned long fps_counter = 0;
6761     static int fps_frames = 0;
6762     unsigned long fps_delay_ms = Counter() - fps_counter;
6763
6764     fps_frames++;
6765
6766     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
6767     {
6768       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6769
6770       fps_frames = 0;
6771       fps_counter = Counter();
6772     }
6773
6774     redraw_mask |= REDRAW_FPS;
6775   }
6776
6777 #if 0
6778   if (stored_player[0].jx != stored_player[0].last_jx ||
6779       stored_player[0].jy != stored_player[0].last_jy)
6780     printf("::: %d, %d, %d, %d, %d\n",
6781            stored_player[0].MovDir,
6782            stored_player[0].MovPos,
6783            stored_player[0].GfxPos,
6784            stored_player[0].Frame,
6785            stored_player[0].StepFrame);
6786 #endif
6787
6788 #if 1
6789   FrameCounter++;
6790   TimeFrames++;
6791
6792   for (i=0; i<MAX_PLAYERS; i++)
6793   {
6794     int move_frames =
6795       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
6796
6797     stored_player[i].Frame += move_frames;
6798
6799     if (stored_player[i].MovPos != 0)
6800       stored_player[i].StepFrame += move_frames;
6801   }
6802 #endif
6803
6804 #if 1
6805   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6806   {
6807     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6808
6809     local_player->show_envelope = 0;
6810   }
6811 #endif
6812 }
6813
6814 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6815 {
6816   int min_x = x, min_y = y, max_x = x, max_y = y;
6817   int i;
6818
6819   for (i=0; i<MAX_PLAYERS; i++)
6820   {
6821     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6822
6823     if (!stored_player[i].active || &stored_player[i] == player)
6824       continue;
6825
6826     min_x = MIN(min_x, jx);
6827     min_y = MIN(min_y, jy);
6828     max_x = MAX(max_x, jx);
6829     max_y = MAX(max_y, jy);
6830   }
6831
6832   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6833 }
6834
6835 static boolean AllPlayersInVisibleScreen()
6836 {
6837   int i;
6838
6839   for (i=0; i<MAX_PLAYERS; i++)
6840   {
6841     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6842
6843     if (!stored_player[i].active)
6844       continue;
6845
6846     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6847       return FALSE;
6848   }
6849
6850   return TRUE;
6851 }
6852
6853 void ScrollLevel(int dx, int dy)
6854 {
6855   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6856   int x, y;
6857
6858   BlitBitmap(drawto_field, drawto_field,
6859              FX + TILEX * (dx == -1) - softscroll_offset,
6860              FY + TILEY * (dy == -1) - softscroll_offset,
6861              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6862              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6863              FX + TILEX * (dx == 1) - softscroll_offset,
6864              FY + TILEY * (dy == 1) - softscroll_offset);
6865
6866   if (dx)
6867   {
6868     x = (dx == 1 ? BX1 : BX2);
6869     for (y=BY1; y <= BY2; y++)
6870       DrawScreenField(x, y);
6871   }
6872
6873   if (dy)
6874   {
6875     y = (dy == 1 ? BY1 : BY2);
6876     for (x=BX1; x <= BX2; x++)
6877       DrawScreenField(x, y);
6878   }
6879
6880   redraw_mask |= REDRAW_FIELD;
6881 }
6882
6883 static void CheckGravityMovement(struct PlayerInfo *player)
6884 {
6885   if (game.gravity && !player->programmed_action)
6886   {
6887     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6888     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6889     int move_dir =
6890       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6891        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6892        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6893     int jx = player->jx, jy = player->jy;
6894     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6895     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6896     int new_jx = jx + dx, new_jy = jy + dy;
6897     boolean field_under_player_is_free =
6898       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6899     boolean player_is_moving_to_valid_field =
6900       (IN_LEV_FIELD(new_jx, new_jy) &&
6901        (Feld[new_jx][new_jy] == EL_SP_BASE ||
6902         Feld[new_jx][new_jy] == EL_SAND));
6903     /* !!! extend EL_SAND to anything diggable !!! */
6904
6905     if (field_under_player_is_free &&
6906         !player_is_moving_to_valid_field &&
6907         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6908       player->programmed_action = MV_DOWN;
6909   }
6910 }
6911
6912 /*
6913   MovePlayerOneStep()
6914   -----------------------------------------------------------------------------
6915   dx, dy:               direction (non-diagonal) to try to move the player to
6916   real_dx, real_dy:     direction as read from input device (can be diagonal)
6917 */
6918
6919 boolean MovePlayerOneStep(struct PlayerInfo *player,
6920                           int dx, int dy, int real_dx, int real_dy)
6921 {
6922 #if 0
6923   static int change_sides[4][2] =
6924   {
6925     /* enter side        leave side */
6926     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
6927     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
6928     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
6929     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
6930   };
6931   int move_direction = (dx == -1 ? MV_LEFT :
6932                         dx == +1 ? MV_RIGHT :
6933                         dy == -1 ? MV_UP :
6934                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
6935   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6936   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6937 #endif
6938   int jx = player->jx, jy = player->jy;
6939   int new_jx = jx + dx, new_jy = jy + dy;
6940   int element;
6941   int can_move;
6942
6943   if (!player->active || (!dx && !dy))
6944     return MF_NO_ACTION;
6945
6946   player->MovDir = (dx < 0 ? MV_LEFT :
6947                     dx > 0 ? MV_RIGHT :
6948                     dy < 0 ? MV_UP :
6949                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6950
6951   if (!IN_LEV_FIELD(new_jx, new_jy))
6952     return MF_NO_ACTION;
6953
6954   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6955     return MF_NO_ACTION;
6956
6957 #if 0
6958   element = MovingOrBlocked2Element(new_jx, new_jy);
6959 #else
6960   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6961 #endif
6962
6963   if (DONT_RUN_INTO(element))
6964   {
6965     if (element == EL_ACID && dx == 0 && dy == 1)
6966     {
6967       SplashAcid(jx, jy);
6968       Feld[jx][jy] = EL_PLAYER_1;
6969       InitMovingField(jx, jy, MV_DOWN);
6970       Store[jx][jy] = EL_ACID;
6971       ContinueMoving(jx, jy);
6972       BuryHero(player);
6973     }
6974     else
6975       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6976
6977     return MF_MOVING;
6978   }
6979
6980   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6981   if (can_move != MF_MOVING)
6982     return can_move;
6983
6984   /* check if DigField() has caused relocation of the player */
6985   if (player->jx != jx || player->jy != jy)
6986     return MF_NO_ACTION;
6987
6988   StorePlayer[jx][jy] = 0;
6989   player->last_jx = jx;
6990   player->last_jy = jy;
6991   player->jx = new_jx;
6992   player->jy = new_jy;
6993   StorePlayer[new_jx][new_jy] = player->element_nr;
6994
6995   player->MovPos =
6996     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6997
6998   ScrollPlayer(player, SCROLL_INIT);
6999
7000 #if 0
7001   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7002   {
7003     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7004                                     CE_OTHER_GETS_LEFT);
7005     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7006                            CE_LEFT_BY_PLAYER, -1);
7007   }
7008
7009   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7010   {
7011     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7012                                     enter_side, CE_OTHER_GETS_ENTERED);
7013     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7014                            CE_ENTERED_BY_PLAYER, -1);
7015   }
7016 #endif
7017
7018   return MF_MOVING;
7019 }
7020
7021 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7022 {
7023   int jx = player->jx, jy = player->jy;
7024   int old_jx = jx, old_jy = jy;
7025   int moved = MF_NO_ACTION;
7026
7027   if (!player->active || (!dx && !dy))
7028     return FALSE;
7029
7030 #if 0
7031   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7032       !tape.playing)
7033     return FALSE;
7034 #else
7035   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7036       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7037     return FALSE;
7038 #endif
7039
7040   /* remove the last programmed player action */
7041   player->programmed_action = 0;
7042
7043   if (player->MovPos)
7044   {
7045     /* should only happen if pre-1.2 tape recordings are played */
7046     /* this is only for backward compatibility */
7047
7048     int original_move_delay_value = player->move_delay_value;
7049
7050 #if DEBUG
7051     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7052            tape.counter);
7053 #endif
7054
7055     /* scroll remaining steps with finest movement resolution */
7056     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7057
7058     while (player->MovPos)
7059     {
7060       ScrollPlayer(player, SCROLL_GO_ON);
7061       ScrollScreen(NULL, SCROLL_GO_ON);
7062       FrameCounter++;
7063       DrawAllPlayers();
7064       BackToFront();
7065     }
7066
7067     player->move_delay_value = original_move_delay_value;
7068   }
7069
7070   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7071   {
7072     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7073       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7074   }
7075   else
7076   {
7077     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7078       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7079   }
7080
7081   jx = player->jx;
7082   jy = player->jy;
7083
7084   if (moved & MF_MOVING && !ScreenMovPos &&
7085       (player == local_player || !options.network))
7086   {
7087     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7088     int offset = (setup.scroll_delay ? 3 : 0);
7089
7090     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7091     {
7092       /* actual player has left the screen -- scroll in that direction */
7093       if (jx != old_jx)         /* player has moved horizontally */
7094         scroll_x += (jx - old_jx);
7095       else                      /* player has moved vertically */
7096         scroll_y += (jy - old_jy);
7097     }
7098     else
7099     {
7100       if (jx != old_jx)         /* player has moved horizontally */
7101       {
7102         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7103             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7104           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7105
7106         /* don't scroll over playfield boundaries */
7107         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7108           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7109
7110         /* don't scroll more than one field at a time */
7111         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7112
7113         /* don't scroll against the player's moving direction */
7114         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7115             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7116           scroll_x = old_scroll_x;
7117       }
7118       else                      /* player has moved vertically */
7119       {
7120         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7121             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7122           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7123
7124         /* don't scroll over playfield boundaries */
7125         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7126           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7127
7128         /* don't scroll more than one field at a time */
7129         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7130
7131         /* don't scroll against the player's moving direction */
7132         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7133             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7134           scroll_y = old_scroll_y;
7135       }
7136     }
7137
7138     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7139     {
7140       if (!options.network && !AllPlayersInVisibleScreen())
7141       {
7142         scroll_x = old_scroll_x;
7143         scroll_y = old_scroll_y;
7144       }
7145       else
7146       {
7147         ScrollScreen(player, SCROLL_INIT);
7148         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7149       }
7150     }
7151   }
7152
7153 #if 0
7154 #if 1
7155   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7156 #else
7157   if (!(moved & MF_MOVING) && !player->is_pushing)
7158     player->Frame = 0;
7159 #endif
7160 #endif
7161
7162   player->StepFrame = 0;
7163
7164   if (moved & MF_MOVING)
7165   {
7166     if (old_jx != jx && old_jy == jy)
7167       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7168     else if (old_jx == jx && old_jy != jy)
7169       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7170
7171     DrawLevelField(jx, jy);     /* for "crumbled sand" */
7172
7173     player->last_move_dir = player->MovDir;
7174     player->is_moving = TRUE;
7175 #if 1
7176     player->is_snapping = FALSE;
7177 #endif
7178
7179 #if 1
7180     player->is_switching = FALSE;
7181 #endif
7182
7183
7184 #if 1
7185     {
7186       static int change_sides[4][2] =
7187       {
7188         /* enter side           leave side */
7189         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
7190         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
7191         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
7192         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
7193       };
7194       int move_direction = player->MovDir;
7195       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7196       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7197
7198 #if 1
7199       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7200       {
7201         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7202                                         leave_side, CE_OTHER_GETS_LEFT);
7203         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7204                                leave_side, CE_LEFT_BY_PLAYER, -1);
7205       }
7206
7207       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7208       {
7209         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7210                                         enter_side, CE_OTHER_GETS_ENTERED);
7211         CheckElementSideChange(jx, jy, Feld[jx][jy],
7212                                enter_side, CE_ENTERED_BY_PLAYER, -1);
7213       }
7214 #endif
7215
7216     }
7217 #endif
7218
7219
7220   }
7221   else
7222   {
7223     CheckGravityMovement(player);
7224
7225     /*
7226     player->last_move_dir = MV_NO_MOVING;
7227     */
7228     player->is_moving = FALSE;
7229   }
7230
7231   if (game.engine_version < VERSION_IDENT(3,0,7,0))
7232   {
7233     TestIfHeroTouchesBadThing(jx, jy);
7234     TestIfPlayerTouchesCustomElement(jx, jy);
7235   }
7236
7237   if (!player->active)
7238     RemoveHero(player);
7239
7240   return moved;
7241 }
7242
7243 void ScrollPlayer(struct PlayerInfo *player, int mode)
7244 {
7245   int jx = player->jx, jy = player->jy;
7246   int last_jx = player->last_jx, last_jy = player->last_jy;
7247   int move_stepsize = TILEX / player->move_delay_value;
7248
7249   if (!player->active || !player->MovPos)
7250     return;
7251
7252   if (mode == SCROLL_INIT)
7253   {
7254     player->actual_frame_counter = FrameCounter;
7255     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7256
7257     if (Feld[last_jx][last_jy] == EL_EMPTY)
7258       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7259
7260 #if 0
7261     DrawPlayer(player);
7262 #endif
7263     return;
7264   }
7265   else if (!FrameReached(&player->actual_frame_counter, 1))
7266     return;
7267
7268   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7269   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7270
7271   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7272     Feld[last_jx][last_jy] = EL_EMPTY;
7273
7274   /* before DrawPlayer() to draw correct player graphic for this case */
7275   if (player->MovPos == 0)
7276     CheckGravityMovement(player);
7277
7278 #if 0
7279   DrawPlayer(player);   /* needed here only to cleanup last field */
7280 #endif
7281
7282   if (player->MovPos == 0)      /* player reached destination field */
7283   {
7284     if (IS_PASSABLE(Feld[last_jx][last_jy]))
7285     {
7286       /* continue with normal speed after quickly moving through gate */
7287       HALVE_PLAYER_SPEED(player);
7288
7289       /* be able to make the next move without delay */
7290       player->move_delay = 0;
7291     }
7292
7293     player->last_jx = jx;
7294     player->last_jy = jy;
7295
7296     if (Feld[jx][jy] == EL_EXIT_OPEN ||
7297         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7298         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
7299     {
7300       DrawPlayer(player);       /* needed here only to cleanup last field */
7301       RemoveHero(player);
7302
7303       if (local_player->friends_still_needed == 0 ||
7304           IS_SP_ELEMENT(Feld[jx][jy]))
7305         player->LevelSolved = player->GameOver = TRUE;
7306     }
7307
7308     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7309     {
7310       TestIfHeroTouchesBadThing(jx, jy);
7311       TestIfPlayerTouchesCustomElement(jx, jy);
7312 #if 1
7313       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
7314 #endif
7315
7316       if (!player->active)
7317         RemoveHero(player);
7318     }
7319
7320     if (tape.single_step && tape.recording && !tape.pausing &&
7321         !player->programmed_action)
7322       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7323   }
7324 }
7325
7326 void ScrollScreen(struct PlayerInfo *player, int mode)
7327 {
7328   static unsigned long screen_frame_counter = 0;
7329
7330   if (mode == SCROLL_INIT)
7331   {
7332     /* set scrolling step size according to actual player's moving speed */
7333     ScrollStepSize = TILEX / player->move_delay_value;
7334
7335     screen_frame_counter = FrameCounter;
7336     ScreenMovDir = player->MovDir;
7337     ScreenMovPos = player->MovPos;
7338     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7339     return;
7340   }
7341   else if (!FrameReached(&screen_frame_counter, 1))
7342     return;
7343
7344   if (ScreenMovPos)
7345   {
7346     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7347     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7348     redraw_mask |= REDRAW_FIELD;
7349   }
7350   else
7351     ScreenMovDir = MV_NO_MOVING;
7352 }
7353
7354 void TestIfPlayerTouchesCustomElement(int x, int y)
7355 {
7356   static int xy[4][2] =
7357   {
7358     { 0, -1 },
7359     { -1, 0 },
7360     { +1, 0 },
7361     { 0, +1 }
7362   };
7363   static int change_sides[4][2] =
7364   {
7365     /* center side       border side */
7366     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7367     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7368     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7369     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7370   };
7371   static int touch_dir[4] =
7372   {
7373     MV_LEFT | MV_RIGHT,
7374     MV_UP   | MV_DOWN,
7375     MV_UP   | MV_DOWN,
7376     MV_LEFT | MV_RIGHT
7377   };
7378   int center_element = Feld[x][y];      /* should always be non-moving! */
7379   int i;
7380
7381   for (i=0; i<4; i++)
7382   {
7383     int xx = x + xy[i][0];
7384     int yy = y + xy[i][1];
7385     int center_side = change_sides[i][0];
7386     int border_side = change_sides[i][1];
7387     int border_element;
7388
7389     if (!IN_LEV_FIELD(xx, yy))
7390       continue;
7391
7392     if (IS_PLAYER(x, y))
7393     {
7394       if (game.engine_version < VERSION_IDENT(3,0,7,0))
7395         border_element = Feld[xx][yy];          /* may be moving! */
7396       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7397         border_element = Feld[xx][yy];
7398       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7399         border_element = MovingOrBlocked2Element(xx, yy);
7400       else
7401         continue;               /* center and border element do not touch */
7402
7403       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7404                                       CE_OTHER_GETS_TOUCHED);
7405       CheckElementSideChange(xx, yy, border_element, border_side,
7406                              CE_TOUCHED_BY_PLAYER, -1);
7407     }
7408     else if (IS_PLAYER(xx, yy))
7409     {
7410       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7411       {
7412         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7413
7414         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7415           continue;             /* center and border element do not touch */
7416       }
7417
7418       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7419                                       CE_OTHER_GETS_TOUCHED);
7420       CheckElementSideChange(x, y, center_element, center_side,
7421                              CE_TOUCHED_BY_PLAYER, -1);
7422
7423       break;
7424     }
7425   }
7426 }
7427
7428 void TestIfElementTouchesCustomElement(int x, int y)
7429 {
7430   static int xy[4][2] =
7431   {
7432     { 0, -1 },
7433     { -1, 0 },
7434     { +1, 0 },
7435     { 0, +1 }
7436   };
7437   static int change_sides[4][2] =
7438   {
7439     /* center side       border side */
7440     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7441     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7442     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7443     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7444   };
7445   static int touch_dir[4] =
7446   {
7447     MV_LEFT | MV_RIGHT,
7448     MV_UP   | MV_DOWN,
7449     MV_UP   | MV_DOWN,
7450     MV_LEFT | MV_RIGHT
7451   };
7452   boolean change_center_element = FALSE;
7453   int center_element_change_page = 0;
7454   int center_element = Feld[x][y];      /* should always be non-moving! */
7455   int i, j;
7456
7457   for (i=0; i<4; i++)
7458   {
7459     int xx = x + xy[i][0];
7460     int yy = y + xy[i][1];
7461     int center_side = change_sides[i][0];
7462     int border_side = change_sides[i][1];
7463     int border_element;
7464
7465     if (!IN_LEV_FIELD(xx, yy))
7466       continue;
7467
7468     if (game.engine_version < VERSION_IDENT(3,0,7,0))
7469       border_element = Feld[xx][yy];    /* may be moving! */
7470     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7471       border_element = Feld[xx][yy];
7472     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7473       border_element = MovingOrBlocked2Element(xx, yy);
7474     else
7475       continue;                 /* center and border element do not touch */
7476
7477     /* check for change of center element (but change it only once) */
7478     if (IS_CUSTOM_ELEMENT(center_element) &&
7479         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7480         !change_center_element)
7481     {
7482       for (j=0; j < element_info[center_element].num_change_pages; j++)
7483       {
7484         struct ElementChangeInfo *change =
7485           &element_info[center_element].change_page[j];
7486
7487         if (change->can_change &&
7488             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7489             change->sides & border_side &&
7490             change->trigger_element == border_element)
7491         {
7492           change_center_element = TRUE;
7493           center_element_change_page = j;
7494
7495           break;
7496         }
7497       }
7498     }
7499
7500     /* check for change of border element */
7501     if (IS_CUSTOM_ELEMENT(border_element) &&
7502         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7503     {
7504       for (j=0; j < element_info[border_element].num_change_pages; j++)
7505       {
7506         struct ElementChangeInfo *change =
7507           &element_info[border_element].change_page[j];
7508
7509         if (change->can_change &&
7510             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7511             change->sides & center_side &&
7512             change->trigger_element == center_element)
7513         {
7514           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7515                                  CE_OTHER_IS_TOUCHING, j);
7516           break;
7517         }
7518       }
7519     }
7520   }
7521
7522   if (change_center_element)
7523     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7524                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7525 }
7526
7527 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7528 {
7529   int i, kill_x = -1, kill_y = -1;
7530   static int test_xy[4][2] =
7531   {
7532     { 0, -1 },
7533     { -1, 0 },
7534     { +1, 0 },
7535     { 0, +1 }
7536   };
7537   static int test_dir[4] =
7538   {
7539     MV_UP,
7540     MV_LEFT,
7541     MV_RIGHT,
7542     MV_DOWN
7543   };
7544
7545   for (i=0; i<4; i++)
7546   {
7547     int test_x, test_y, test_move_dir, test_element;
7548
7549     test_x = good_x + test_xy[i][0];
7550     test_y = good_y + test_xy[i][1];
7551     if (!IN_LEV_FIELD(test_x, test_y))
7552       continue;
7553
7554     test_move_dir =
7555       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7556
7557 #if 0
7558     test_element = Feld[test_x][test_y];
7559 #else
7560     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7561 #endif
7562
7563     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7564        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7565     */
7566     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7567         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
7568     {
7569       kill_x = test_x;
7570       kill_y = test_y;
7571       break;
7572     }
7573   }
7574
7575   if (kill_x != -1 || kill_y != -1)
7576   {
7577     if (IS_PLAYER(good_x, good_y))
7578     {
7579       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7580
7581       if (player->shield_deadly_time_left > 0)
7582         Bang(kill_x, kill_y);
7583       else if (!PLAYER_PROTECTED(good_x, good_y))
7584         KillHero(player);
7585     }
7586     else
7587       Bang(good_x, good_y);
7588   }
7589 }
7590
7591 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7592 {
7593   int i, kill_x = -1, kill_y = -1;
7594   int bad_element = Feld[bad_x][bad_y];
7595   static int test_xy[4][2] =
7596   {
7597     { 0, -1 },
7598     { -1, 0 },
7599     { +1, 0 },
7600     { 0, +1 }
7601   };
7602   static int touch_dir[4] =
7603   {
7604     MV_LEFT | MV_RIGHT,
7605     MV_UP   | MV_DOWN,
7606     MV_UP   | MV_DOWN,
7607     MV_LEFT | MV_RIGHT
7608   };
7609   static int test_dir[4] =
7610   {
7611     MV_UP,
7612     MV_LEFT,
7613     MV_RIGHT,
7614     MV_DOWN
7615   };
7616
7617   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
7618     return;
7619
7620   for (i=0; i<4; i++)
7621   {
7622     int test_x, test_y, test_move_dir, test_element;
7623
7624     test_x = bad_x + test_xy[i][0];
7625     test_y = bad_y + test_xy[i][1];
7626     if (!IN_LEV_FIELD(test_x, test_y))
7627       continue;
7628
7629     test_move_dir =
7630       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7631
7632     test_element = Feld[test_x][test_y];
7633
7634     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7635        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7636     */
7637     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
7638         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
7639     {
7640       /* good thing is player or penguin that does not move away */
7641       if (IS_PLAYER(test_x, test_y))
7642       {
7643         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7644
7645         if (bad_element == EL_ROBOT && player->is_moving)
7646           continue;     /* robot does not kill player if he is moving */
7647
7648         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7649         {
7650           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7651             continue;           /* center and border element do not touch */
7652         }
7653
7654         kill_x = test_x;
7655         kill_y = test_y;
7656         break;
7657       }
7658       else if (test_element == EL_PENGUIN)
7659       {
7660         kill_x = test_x;
7661         kill_y = test_y;
7662         break;
7663       }
7664     }
7665   }
7666
7667   if (kill_x != -1 || kill_y != -1)
7668   {
7669     if (IS_PLAYER(kill_x, kill_y))
7670     {
7671       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7672
7673       if (player->shield_deadly_time_left > 0)
7674         Bang(bad_x, bad_y);
7675       else if (!PLAYER_PROTECTED(kill_x, kill_y))
7676         KillHero(player);
7677     }
7678     else
7679       Bang(kill_x, kill_y);
7680   }
7681 }
7682
7683 void TestIfHeroTouchesBadThing(int x, int y)
7684 {
7685   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7686 }
7687
7688 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7689 {
7690   TestIfGoodThingHitsBadThing(x, y, move_dir);
7691 }
7692
7693 void TestIfBadThingTouchesHero(int x, int y)
7694 {
7695   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7696 }
7697
7698 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7699 {
7700   TestIfBadThingHitsGoodThing(x, y, move_dir);
7701 }
7702
7703 void TestIfFriendTouchesBadThing(int x, int y)
7704 {
7705   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7706 }
7707
7708 void TestIfBadThingTouchesFriend(int x, int y)
7709 {
7710   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7711 }
7712
7713 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7714 {
7715   int i, kill_x = bad_x, kill_y = bad_y;
7716   static int xy[4][2] =
7717   {
7718     { 0, -1 },
7719     { -1, 0 },
7720     { +1, 0 },
7721     { 0, +1 }
7722   };
7723
7724   for (i=0; i<4; i++)
7725   {
7726     int x, y, element;
7727
7728     x = bad_x + xy[i][0];
7729     y = bad_y + xy[i][1];
7730     if (!IN_LEV_FIELD(x, y))
7731       continue;
7732
7733     element = Feld[x][y];
7734     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7735         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7736     {
7737       kill_x = x;
7738       kill_y = y;
7739       break;
7740     }
7741   }
7742
7743   if (kill_x != bad_x || kill_y != bad_y)
7744     Bang(bad_x, bad_y);
7745 }
7746
7747 void KillHero(struct PlayerInfo *player)
7748 {
7749   int jx = player->jx, jy = player->jy;
7750
7751   if (!player->active)
7752     return;
7753
7754   /* remove accessible field at the player's position */
7755   Feld[jx][jy] = EL_EMPTY;
7756
7757   /* deactivate shield (else Bang()/Explode() would not work right) */
7758   player->shield_normal_time_left = 0;
7759   player->shield_deadly_time_left = 0;
7760
7761   Bang(jx, jy);
7762   BuryHero(player);
7763 }
7764
7765 static void KillHeroUnlessProtected(int x, int y)
7766 {
7767   if (!PLAYER_PROTECTED(x, y))
7768     KillHero(PLAYERINFO(x, y));
7769 }
7770
7771 void BuryHero(struct PlayerInfo *player)
7772 {
7773   int jx = player->jx, jy = player->jy;
7774
7775   if (!player->active)
7776     return;
7777
7778 #if 1
7779   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
7780 #else
7781   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
7782 #endif
7783   PlayLevelSound(jx, jy, SND_GAME_LOSING);
7784
7785   player->GameOver = TRUE;
7786   RemoveHero(player);
7787 }
7788
7789 void RemoveHero(struct PlayerInfo *player)
7790 {
7791   int jx = player->jx, jy = player->jy;
7792   int i, found = FALSE;
7793
7794   player->present = FALSE;
7795   player->active = FALSE;
7796
7797   if (!ExplodeField[jx][jy])
7798     StorePlayer[jx][jy] = 0;
7799
7800   for (i=0; i<MAX_PLAYERS; i++)
7801     if (stored_player[i].active)
7802       found = TRUE;
7803
7804   if (!found)
7805     AllPlayersGone = TRUE;
7806
7807   ExitX = ZX = jx;
7808   ExitY = ZY = jy;
7809 }
7810
7811 /*
7812   =============================================================================
7813   checkDiagonalPushing()
7814   -----------------------------------------------------------------------------
7815   check if diagonal input device direction results in pushing of object
7816   (by checking if the alternative direction is walkable, diggable, ...)
7817   =============================================================================
7818 */
7819
7820 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7821                                     int x, int y, int real_dx, int real_dy)
7822 {
7823   int jx, jy, dx, dy, xx, yy;
7824
7825   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
7826     return TRUE;
7827
7828   /* diagonal direction: check alternative direction */
7829   jx = player->jx;
7830   jy = player->jy;
7831   dx = x - jx;
7832   dy = y - jy;
7833   xx = jx + (dx == 0 ? real_dx : 0);
7834   yy = jy + (dy == 0 ? real_dy : 0);
7835
7836   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7837 }
7838
7839 /*
7840   =============================================================================
7841   DigField()
7842   -----------------------------------------------------------------------------
7843   x, y:                 field next to player (non-diagonal) to try to dig to
7844   real_dx, real_dy:     direction as read from input device (can be diagonal)
7845   =============================================================================
7846 */
7847
7848 int DigField(struct PlayerInfo *player,
7849              int x, int y, int real_dx, int real_dy, int mode)
7850 {
7851   static int change_sides[4] =
7852   {
7853     CH_SIDE_RIGHT,      /* moving left  */
7854     CH_SIDE_LEFT,       /* moving right */
7855     CH_SIDE_BOTTOM,     /* moving up    */
7856     CH_SIDE_TOP,        /* moving down  */
7857   };
7858   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7859   int jx = player->jx, jy = player->jy;
7860   int dx = x - jx, dy = y - jy;
7861   int nextx = x + dx, nexty = y + dy;
7862   int move_direction = (dx == -1 ? MV_LEFT :
7863                         dx == +1 ? MV_RIGHT :
7864                         dy == -1 ? MV_UP :
7865                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7866   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7867   int element;
7868
7869   if (player->MovPos == 0)
7870   {
7871     player->is_digging = FALSE;
7872     player->is_collecting = FALSE;
7873   }
7874
7875   if (player->MovPos == 0)      /* last pushing move finished */
7876     player->is_pushing = FALSE;
7877
7878   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
7879   {
7880     player->is_switching = FALSE;
7881     player->push_delay = 0;
7882
7883     return MF_NO_ACTION;
7884   }
7885
7886   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7887     return MF_NO_ACTION;
7888
7889 #if 0
7890   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7891 #else
7892   if (IS_TUBE(Feld[jx][jy]) ||
7893       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7894 #endif
7895   {
7896     int i = 0;
7897     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7898     int tube_leave_directions[][2] =
7899     {
7900       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7901       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
7902       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
7903       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
7904       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
7905       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
7906       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
7907       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
7908       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
7909       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
7910       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
7911       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7912     };
7913
7914     while (tube_leave_directions[i][0] != tube_element)
7915     {
7916       i++;
7917       if (tube_leave_directions[i][0] == -1)    /* should not happen */
7918         break;
7919     }
7920
7921     if (!(tube_leave_directions[i][1] & move_direction))
7922       return MF_NO_ACTION;      /* tube has no opening in this direction */
7923   }
7924
7925   element = Feld[x][y];
7926
7927   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7928       game.engine_version >= VERSION_IDENT(2,2,0,0))
7929     return MF_NO_ACTION;
7930
7931   switch (element)
7932   {
7933     case EL_SP_PORT_LEFT:
7934     case EL_SP_PORT_RIGHT:
7935     case EL_SP_PORT_UP:
7936     case EL_SP_PORT_DOWN:
7937     case EL_SP_PORT_HORIZONTAL:
7938     case EL_SP_PORT_VERTICAL:
7939     case EL_SP_PORT_ANY:
7940     case EL_SP_GRAVITY_PORT_LEFT:
7941     case EL_SP_GRAVITY_PORT_RIGHT:
7942     case EL_SP_GRAVITY_PORT_UP:
7943     case EL_SP_GRAVITY_PORT_DOWN:
7944       if ((dx == -1 &&
7945            element != EL_SP_PORT_LEFT &&
7946            element != EL_SP_GRAVITY_PORT_LEFT &&
7947            element != EL_SP_PORT_HORIZONTAL &&
7948            element != EL_SP_PORT_ANY) ||
7949           (dx == +1 &&
7950            element != EL_SP_PORT_RIGHT &&
7951            element != EL_SP_GRAVITY_PORT_RIGHT &&
7952            element != EL_SP_PORT_HORIZONTAL &&
7953            element != EL_SP_PORT_ANY) ||
7954           (dy == -1 &&
7955            element != EL_SP_PORT_UP &&
7956            element != EL_SP_GRAVITY_PORT_UP &&
7957            element != EL_SP_PORT_VERTICAL &&
7958            element != EL_SP_PORT_ANY) ||
7959           (dy == +1 &&
7960            element != EL_SP_PORT_DOWN &&
7961            element != EL_SP_GRAVITY_PORT_DOWN &&
7962            element != EL_SP_PORT_VERTICAL &&
7963            element != EL_SP_PORT_ANY) ||
7964           !IN_LEV_FIELD(nextx, nexty) ||
7965           !IS_FREE(nextx, nexty))
7966         return MF_NO_ACTION;
7967
7968       if (element == EL_SP_GRAVITY_PORT_LEFT ||
7969           element == EL_SP_GRAVITY_PORT_RIGHT ||
7970           element == EL_SP_GRAVITY_PORT_UP ||
7971           element == EL_SP_GRAVITY_PORT_DOWN)
7972         game.gravity = !game.gravity;
7973
7974       /* automatically move to the next field with double speed */
7975       player->programmed_action = move_direction;
7976       DOUBLE_PLAYER_SPEED(player);
7977
7978       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
7979       break;
7980
7981     case EL_TUBE_ANY:
7982     case EL_TUBE_VERTICAL:
7983     case EL_TUBE_HORIZONTAL:
7984     case EL_TUBE_VERTICAL_LEFT:
7985     case EL_TUBE_VERTICAL_RIGHT:
7986     case EL_TUBE_HORIZONTAL_UP:
7987     case EL_TUBE_HORIZONTAL_DOWN:
7988     case EL_TUBE_LEFT_UP:
7989     case EL_TUBE_LEFT_DOWN:
7990     case EL_TUBE_RIGHT_UP:
7991     case EL_TUBE_RIGHT_DOWN:
7992       {
7993         int i = 0;
7994         int tube_enter_directions[][2] =
7995         {
7996           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7997           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
7998           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
7999           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
8000           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
8001           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
8002           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
8003           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
8004           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
8005           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
8006           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
8007           { -1,                         MV_NO_MOVING                         }
8008         };
8009
8010         while (tube_enter_directions[i][0] != element)
8011         {
8012           i++;
8013           if (tube_enter_directions[i][0] == -1)        /* should not happen */
8014             break;
8015         }
8016
8017         if (!(tube_enter_directions[i][1] & move_direction))
8018           return MF_NO_ACTION;  /* tube has no opening in this direction */
8019
8020         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
8021       }
8022       break;
8023
8024     default:
8025
8026       if (IS_WALKABLE(element))
8027       {
8028         int sound_action = ACTION_WALKING;
8029
8030         if (element >= EL_GATE_1 && element <= EL_GATE_4)
8031         {
8032           if (!player->key[element - EL_GATE_1])
8033             return MF_NO_ACTION;
8034         }
8035         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
8036         {
8037           if (!player->key[element - EL_GATE_1_GRAY])
8038             return MF_NO_ACTION;
8039         }
8040         else if (element == EL_EXIT_OPEN ||
8041                  element == EL_SP_EXIT_OPEN ||
8042                  element == EL_SP_EXIT_OPENING)
8043         {
8044           sound_action = ACTION_PASSING;        /* player is passing exit */
8045         }
8046         else if (element == EL_EMPTY)
8047         {
8048           sound_action = ACTION_MOVING;         /* nothing to walk on */
8049         }
8050
8051         /* play sound from background or player, whatever is available */
8052         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
8053           PlayLevelSoundElementAction(x, y, element, sound_action);
8054         else
8055           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
8056
8057         break;
8058       }
8059       else if (IS_PASSABLE(element))
8060       {
8061         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
8062           return MF_NO_ACTION;
8063
8064 #if 1
8065         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
8066           return MF_NO_ACTION;
8067 #endif
8068
8069         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
8070         {
8071           if (!player->key[element - EL_EM_GATE_1])
8072             return MF_NO_ACTION;
8073         }
8074         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
8075         {
8076           if (!player->key[element - EL_EM_GATE_1_GRAY])
8077             return MF_NO_ACTION;
8078         }
8079
8080         /* automatically move to the next field with double speed */
8081         player->programmed_action = move_direction;
8082         DOUBLE_PLAYER_SPEED(player);
8083
8084         PlayLevelSoundAction(x, y, ACTION_PASSING);
8085
8086         break;
8087       }
8088       else if (IS_DIGGABLE(element))
8089       {
8090         RemoveField(x, y);
8091
8092         if (mode != DF_SNAP)
8093         {
8094 #if 1
8095           GfxElement[x][y] = GFX_ELEMENT(element);
8096 #else
8097           GfxElement[x][y] =
8098             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
8099 #endif
8100           player->is_digging = TRUE;
8101         }
8102
8103         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
8104
8105         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
8106
8107 #if 1
8108         if (mode == DF_SNAP)
8109           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8110 #endif
8111
8112         break;
8113       }
8114       else if (IS_COLLECTIBLE(element))
8115       {
8116         RemoveField(x, y);
8117
8118         if (mode != DF_SNAP)
8119         {
8120           GfxElement[x][y] = element;
8121           player->is_collecting = TRUE;
8122         }
8123
8124         if (element == EL_SPEED_PILL)
8125           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
8126         else if (element == EL_EXTRA_TIME && level.time > 0)
8127         {
8128           TimeLeft += 10;
8129           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8130         }
8131         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
8132         {
8133           player->shield_normal_time_left += 10;
8134           if (element == EL_SHIELD_DEADLY)
8135             player->shield_deadly_time_left += 10;
8136         }
8137         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
8138         {
8139           if (player->inventory_size < MAX_INVENTORY_SIZE)
8140             player->inventory_element[player->inventory_size++] = element;
8141
8142           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8143                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8144         }
8145         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
8146         {
8147           player->dynabomb_count++;
8148           player->dynabombs_left++;
8149         }
8150         else if (element == EL_DYNABOMB_INCREASE_SIZE)
8151         {
8152           player->dynabomb_size++;
8153         }
8154         else if (element == EL_DYNABOMB_INCREASE_POWER)
8155         {
8156           player->dynabomb_xl = TRUE;
8157         }
8158         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8159                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8160         {
8161           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8162                         element - EL_KEY_1 : element - EL_EM_KEY_1);
8163
8164           player->key[key_nr] = TRUE;
8165
8166           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8167                              el2edimg(EL_KEY_1 + key_nr));
8168           redraw_mask |= REDRAW_DOOR_1;
8169         }
8170         else if (IS_ENVELOPE(element))
8171         {
8172 #if 1
8173           player->show_envelope = element;
8174 #else
8175           ShowEnvelope(element - EL_ENVELOPE_1);
8176 #endif
8177         }
8178         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8179         {
8180           int i;
8181
8182           for (i=0; i < element_info[element].collect_count; i++)
8183             if (player->inventory_size < MAX_INVENTORY_SIZE)
8184               player->inventory_element[player->inventory_size++] = element;
8185
8186           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8187                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8188         }
8189         else if (element_info[element].collect_count > 0)
8190         {
8191           local_player->gems_still_needed -=
8192             element_info[element].collect_count;
8193           if (local_player->gems_still_needed < 0)
8194             local_player->gems_still_needed = 0;
8195
8196           DrawText(DX_EMERALDS, DY_EMERALDS,
8197                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8198         }
8199
8200         RaiseScoreElement(element);
8201         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
8202
8203         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8204
8205 #if 1
8206         if (mode == DF_SNAP)
8207           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8208 #endif
8209
8210         break;
8211       }
8212       else if (IS_PUSHABLE(element))
8213       {
8214         if (mode == DF_SNAP && element != EL_BD_ROCK)
8215           return MF_NO_ACTION;
8216
8217         if (CAN_FALL(element) && dy)
8218           return MF_NO_ACTION;
8219
8220         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8221             !(element == EL_SPRING && use_spring_bug))
8222           return MF_NO_ACTION;
8223
8224 #if 1
8225         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8226             ((move_direction & MV_VERTICAL &&
8227               ((element_info[element].move_pattern & MV_LEFT &&
8228                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8229                (element_info[element].move_pattern & MV_RIGHT &&
8230                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8231              (move_direction & MV_HORIZONTAL &&
8232               ((element_info[element].move_pattern & MV_UP &&
8233                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8234                (element_info[element].move_pattern & MV_DOWN &&
8235                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8236           return MF_NO_ACTION;
8237 #endif
8238
8239 #if 1
8240         /* do not push elements already moving away faster than player */
8241         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8242             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8243           return MF_NO_ACTION;
8244 #else
8245         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8246           return MF_NO_ACTION;
8247 #endif
8248
8249 #if 1
8250         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8251         {
8252           if (player->push_delay_value == -1)
8253             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8254         }
8255         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8256         {
8257           if (!player->is_pushing)
8258             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8259         }
8260
8261         /*
8262         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8263             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8264              !player_is_pushing))
8265           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8266         */
8267 #else
8268         if (!player->is_pushing &&
8269             game.engine_version >= VERSION_IDENT(2,2,0,7))
8270           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8271 #endif
8272
8273 #if 0
8274         printf("::: push delay: %ld [%d, %d] [%d]\n",
8275                player->push_delay_value, FrameCounter, game.engine_version,
8276                player->is_pushing);
8277 #endif
8278
8279         player->is_pushing = TRUE;
8280
8281         if (!(IN_LEV_FIELD(nextx, nexty) &&
8282               (IS_FREE(nextx, nexty) ||
8283                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8284                 IS_SB_ELEMENT(element)))))
8285           return MF_NO_ACTION;
8286
8287         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8288           return MF_NO_ACTION;
8289
8290         if (player->push_delay == 0)    /* new pushing; restart delay */
8291           player->push_delay = FrameCounter;
8292
8293         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8294             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8295             element != EL_SPRING && element != EL_BALLOON)
8296         {
8297           /* make sure that there is no move delay before next try to push */
8298           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8299             player->move_delay = INITIAL_MOVE_DELAY_OFF;
8300
8301           return MF_NO_ACTION;
8302         }
8303
8304 #if 0
8305         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8306 #endif
8307
8308         if (IS_SB_ELEMENT(element))
8309         {
8310           if (element == EL_SOKOBAN_FIELD_FULL)
8311           {
8312             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8313             local_player->sokobanfields_still_needed++;
8314           }
8315
8316           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8317           {
8318             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8319             local_player->sokobanfields_still_needed--;
8320           }
8321
8322           Feld[x][y] = EL_SOKOBAN_OBJECT;
8323
8324           if (Back[x][y] == Back[nextx][nexty])
8325             PlayLevelSoundAction(x, y, ACTION_PUSHING);
8326           else if (Back[x][y] != 0)
8327             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8328                                         ACTION_EMPTYING);
8329           else
8330             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8331                                         ACTION_FILLING);
8332
8333           if (local_player->sokobanfields_still_needed == 0 &&
8334               game.emulation == EMU_SOKOBAN)
8335           {
8336             player->LevelSolved = player->GameOver = TRUE;
8337             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
8338           }
8339         }
8340         else
8341           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
8342
8343         InitMovingField(x, y, move_direction);
8344         GfxAction[x][y] = ACTION_PUSHING;
8345
8346         if (mode == DF_SNAP)
8347           ContinueMoving(x, y);
8348         else
8349           MovPos[x][y] = (dx != 0 ? dx : dy);
8350
8351         Pushed[x][y] = TRUE;
8352         Pushed[nextx][nexty] = TRUE;
8353
8354         if (game.engine_version < VERSION_IDENT(2,2,0,7))
8355           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8356         else
8357           player->push_delay_value = -1;        /* get new value later */
8358
8359         CheckTriggeredElementSideChange(x, y, element, dig_side,
8360                                         CE_OTHER_GETS_PUSHED);
8361         CheckElementSideChange(x, y, element, dig_side,
8362                                CE_PUSHED_BY_PLAYER, -1);
8363
8364         break;
8365       }
8366       else if (IS_SWITCHABLE(element))
8367       {
8368         if (PLAYER_SWITCHING(player, x, y))
8369           return MF_ACTION;
8370
8371         player->is_switching = TRUE;
8372         player->switch_x = x;
8373         player->switch_y = y;
8374
8375         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
8376
8377         if (element == EL_ROBOT_WHEEL)
8378         {
8379           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8380           ZX = x;
8381           ZY = y;
8382
8383           DrawLevelField(x, y);
8384         }
8385         else if (element == EL_SP_TERMINAL)
8386         {
8387           int xx, yy;
8388
8389           for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8390           {
8391             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8392               Bang(xx, yy);
8393             else if (Feld[xx][yy] == EL_SP_TERMINAL)
8394               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8395           }
8396         }
8397         else if (IS_BELT_SWITCH(element))
8398         {
8399           ToggleBeltSwitch(x, y);
8400         }
8401         else if (element == EL_SWITCHGATE_SWITCH_UP ||
8402                  element == EL_SWITCHGATE_SWITCH_DOWN)
8403         {
8404           ToggleSwitchgateSwitch(x, y);
8405         }
8406         else if (element == EL_LIGHT_SWITCH ||
8407                  element == EL_LIGHT_SWITCH_ACTIVE)
8408         {
8409           ToggleLightSwitch(x, y);
8410
8411 #if 0
8412           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
8413                          SND_LIGHT_SWITCH_ACTIVATING :
8414                          SND_LIGHT_SWITCH_DEACTIVATING);
8415 #endif
8416         }
8417         else if (element == EL_TIMEGATE_SWITCH)
8418         {
8419           ActivateTimegateSwitch(x, y);
8420         }
8421         else if (element == EL_BALLOON_SWITCH_LEFT ||
8422                  element == EL_BALLOON_SWITCH_RIGHT ||
8423                  element == EL_BALLOON_SWITCH_UP ||
8424                  element == EL_BALLOON_SWITCH_DOWN ||
8425                  element == EL_BALLOON_SWITCH_ANY)
8426         {
8427           if (element == EL_BALLOON_SWITCH_ANY)
8428             game.balloon_dir = move_direction;
8429           else
8430             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
8431                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8432                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
8433                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
8434                                 MV_NO_MOVING);
8435         }
8436         else if (element == EL_LAMP)
8437         {
8438           Feld[x][y] = EL_LAMP_ACTIVE;
8439           local_player->lights_still_needed--;
8440
8441           DrawLevelField(x, y);
8442         }
8443         else if (element == EL_TIME_ORB_FULL)
8444         {
8445           Feld[x][y] = EL_TIME_ORB_EMPTY;
8446           TimeLeft += 10;
8447           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8448
8449           DrawLevelField(x, y);
8450
8451 #if 0
8452           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8453 #endif
8454         }
8455
8456         return MF_ACTION;
8457       }
8458       else
8459       {
8460         if (!PLAYER_SWITCHING(player, x, y))
8461         {
8462           player->is_switching = TRUE;
8463           player->switch_x = x;
8464           player->switch_y = y;
8465
8466           CheckTriggeredElementSideChange(x, y, element, dig_side,
8467                                           CE_OTHER_IS_SWITCHING);
8468           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8469         }
8470
8471         CheckTriggeredElementSideChange(x, y, element, dig_side,
8472                                         CE_OTHER_GETS_PRESSED);
8473         CheckElementSideChange(x, y, element, dig_side,
8474                                CE_PRESSED_BY_PLAYER, -1);
8475       }
8476
8477       return MF_NO_ACTION;
8478   }
8479
8480   player->push_delay = 0;
8481
8482   if (Feld[x][y] != element)            /* really digged/collected something */
8483     player->is_collecting = !player->is_digging;
8484
8485   return MF_MOVING;
8486 }
8487
8488 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8489 {
8490   int jx = player->jx, jy = player->jy;
8491   int x = jx + dx, y = jy + dy;
8492   int snap_direction = (dx == -1 ? MV_LEFT :
8493                         dx == +1 ? MV_RIGHT :
8494                         dy == -1 ? MV_UP :
8495                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8496
8497   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8498     return FALSE;
8499
8500   if (!player->active || !IN_LEV_FIELD(x, y))
8501     return FALSE;
8502
8503   if (dx && dy)
8504     return FALSE;
8505
8506   if (!dx && !dy)
8507   {
8508     if (player->MovPos == 0)
8509       player->is_pushing = FALSE;
8510
8511     player->is_snapping = FALSE;
8512
8513     if (player->MovPos == 0)
8514     {
8515       player->is_moving = FALSE;
8516       player->is_digging = FALSE;
8517       player->is_collecting = FALSE;
8518     }
8519
8520     return FALSE;
8521   }
8522
8523   if (player->is_snapping)
8524     return FALSE;
8525
8526   player->MovDir = snap_direction;
8527
8528   player->is_moving = FALSE;
8529   player->is_digging = FALSE;
8530   player->is_collecting = FALSE;
8531
8532   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8533     return FALSE;
8534
8535   player->is_snapping = TRUE;
8536
8537   player->is_moving = FALSE;
8538   player->is_digging = FALSE;
8539   player->is_collecting = FALSE;
8540
8541   DrawLevelField(x, y);
8542   BackToFront();
8543
8544   return TRUE;
8545 }
8546
8547 boolean DropElement(struct PlayerInfo *player)
8548 {
8549   int jx = player->jx, jy = player->jy;
8550   int old_element;
8551
8552   if (!player->active || player->MovPos)
8553     return FALSE;
8554
8555   old_element = Feld[jx][jy];
8556
8557   /* check if player has anything that can be dropped */
8558   if (player->inventory_size == 0 && player->dynabombs_left == 0)
8559     return FALSE;
8560
8561   /* check if anything can be dropped at the current position */
8562   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8563     return FALSE;
8564
8565   /* collected custom elements can only be dropped on empty fields */
8566   if (player->inventory_size > 0 &&
8567       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8568       && old_element != EL_EMPTY)
8569     return FALSE;
8570
8571   if (old_element != EL_EMPTY)
8572     Back[jx][jy] = old_element;         /* store old element on this field */
8573
8574   MovDelay[jx][jy] = 96;
8575
8576   ResetGfxAnimation(jx, jy);
8577   ResetRandomAnimationValue(jx, jy);
8578
8579   if (player->inventory_size > 0)
8580   {
8581     int new_element = player->inventory_element[--player->inventory_size];
8582
8583     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8584                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8585                     new_element);
8586
8587     DrawText(DX_DYNAMITE, DY_DYNAMITE,
8588              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8589
8590     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8591       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8592
8593     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8594
8595     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8596     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8597
8598     TestIfElementTouchesCustomElement(jx, jy);
8599   }
8600   else          /* player is dropping a dyna bomb */
8601   {
8602     player->dynabombs_left--;
8603
8604     Feld[jx][jy] =
8605       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8606
8607     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8608       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8609
8610     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
8611   }
8612
8613   return TRUE;
8614 }
8615
8616 /* ------------------------------------------------------------------------- */
8617 /* game sound playing functions                                              */
8618 /* ------------------------------------------------------------------------- */
8619
8620 static int *loop_sound_frame = NULL;
8621 static int *loop_sound_volume = NULL;
8622
8623 void InitPlayLevelSound()
8624 {
8625   int num_sounds = getSoundListSize();
8626
8627   if (loop_sound_frame != NULL)
8628     free(loop_sound_frame);
8629
8630   if (loop_sound_volume != NULL)
8631     free(loop_sound_volume);
8632
8633   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8634   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8635 }
8636
8637 static void PlayLevelSound(int x, int y, int nr)
8638 {
8639   int sx = SCREENX(x), sy = SCREENY(y);
8640   int volume, stereo_position;
8641   int max_distance = 8;
8642   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8643
8644   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8645       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8646     return;
8647
8648   if (!IN_LEV_FIELD(x, y) ||
8649       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8650       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8651     return;
8652
8653   volume = SOUND_MAX_VOLUME;
8654
8655   if (!IN_SCR_FIELD(sx, sy))
8656   {
8657     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8658     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8659
8660     volume -= volume * (dx > dy ? dx : dy) / max_distance;
8661   }
8662
8663   stereo_position = (SOUND_MAX_LEFT +
8664                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8665                      (SCR_FIELDX + 2 * max_distance));
8666
8667   if (IS_LOOP_SOUND(nr))
8668   {
8669     /* This assures that quieter loop sounds do not overwrite louder ones,
8670        while restarting sound volume comparison with each new game frame. */
8671
8672     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8673       return;
8674
8675     loop_sound_volume[nr] = volume;
8676     loop_sound_frame[nr] = FrameCounter;
8677   }
8678
8679   PlaySoundExt(nr, volume, stereo_position, type);
8680 }
8681
8682 static void PlayLevelSoundNearest(int x, int y, int sound_action)
8683 {
8684   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
8685                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
8686                  y < LEVELY(BY1) ? LEVELY(BY1) :
8687                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
8688                  sound_action);
8689 }
8690
8691 static void PlayLevelSoundAction(int x, int y, int action)
8692 {
8693   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
8694 }
8695
8696 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
8697 {
8698   int sound_effect = element_info[element].sound[action];
8699
8700   if (sound_effect != SND_UNDEFINED)
8701     PlayLevelSound(x, y, sound_effect);
8702 }
8703
8704 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
8705                                               int action)
8706 {
8707   int sound_effect = element_info[element].sound[action];
8708
8709   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8710     PlayLevelSound(x, y, sound_effect);
8711 }
8712
8713 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
8714 {
8715   int sound_effect = element_info[Feld[x][y]].sound[action];
8716
8717   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8718     PlayLevelSound(x, y, sound_effect);
8719 }
8720
8721 static void StopLevelSoundActionIfLoop(int x, int y, int action)
8722 {
8723   int sound_effect = element_info[Feld[x][y]].sound[action];
8724
8725   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8726     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8727 }
8728
8729 static void PlayLevelMusic()
8730 {
8731 #if 1
8732   if (levelset.music[level_nr] != MUS_UNDEFINED)
8733     PlayMusic(levelset.music[level_nr]);        /* from config file */
8734   else
8735     PlayMusic(-(level_nr + 1));                 /* from music dir */
8736 #else
8737   PlayMusic(level_nr);
8738 #endif
8739 }
8740
8741 void RaiseScore(int value)
8742 {
8743   local_player->score += value;
8744   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8745 }
8746
8747 void RaiseScoreElement(int element)
8748 {
8749   switch(element)
8750   {
8751     case EL_EMERALD:
8752     case EL_BD_DIAMOND:
8753     case EL_EMERALD_YELLOW:
8754     case EL_EMERALD_RED:
8755     case EL_EMERALD_PURPLE:
8756     case EL_SP_INFOTRON:
8757       RaiseScore(level.score[SC_EMERALD]);
8758       break;
8759     case EL_DIAMOND:
8760       RaiseScore(level.score[SC_DIAMOND]);
8761       break;
8762     case EL_CRYSTAL:
8763       RaiseScore(level.score[SC_CRYSTAL]);
8764       break;
8765     case EL_PEARL:
8766       RaiseScore(level.score[SC_PEARL]);
8767       break;
8768     case EL_BUG:
8769     case EL_BD_BUTTERFLY:
8770     case EL_SP_ELECTRON:
8771       RaiseScore(level.score[SC_BUG]);
8772       break;
8773     case EL_SPACESHIP:
8774     case EL_BD_FIREFLY:
8775     case EL_SP_SNIKSNAK:
8776       RaiseScore(level.score[SC_SPACESHIP]);
8777       break;
8778     case EL_YAMYAM:
8779     case EL_DARK_YAMYAM:
8780       RaiseScore(level.score[SC_YAMYAM]);
8781       break;
8782     case EL_ROBOT:
8783       RaiseScore(level.score[SC_ROBOT]);
8784       break;
8785     case EL_PACMAN:
8786       RaiseScore(level.score[SC_PACMAN]);
8787       break;
8788     case EL_NUT:
8789       RaiseScore(level.score[SC_NUT]);
8790       break;
8791     case EL_DYNAMITE:
8792     case EL_SP_DISK_RED:
8793     case EL_DYNABOMB_INCREASE_NUMBER:
8794     case EL_DYNABOMB_INCREASE_SIZE:
8795     case EL_DYNABOMB_INCREASE_POWER:
8796       RaiseScore(level.score[SC_DYNAMITE]);
8797       break;
8798     case EL_SHIELD_NORMAL:
8799     case EL_SHIELD_DEADLY:
8800       RaiseScore(level.score[SC_SHIELD]);
8801       break;
8802     case EL_EXTRA_TIME:
8803       RaiseScore(level.score[SC_TIME_BONUS]);
8804       break;
8805     case EL_KEY_1:
8806     case EL_KEY_2:
8807     case EL_KEY_3:
8808     case EL_KEY_4:
8809       RaiseScore(level.score[SC_KEY]);
8810       break;
8811     default:
8812       RaiseScore(element_info[element].collect_score);
8813       break;
8814   }
8815 }
8816
8817 void RequestQuitGame(boolean ask_if_really_quit)
8818 {
8819   if (AllPlayersGone ||
8820       !ask_if_really_quit ||
8821       level_editor_test_game ||
8822       Request("Do you really want to quit the game ?",
8823               REQ_ASK | REQ_STAY_CLOSED))
8824   {
8825 #if defined(PLATFORM_UNIX)
8826     if (options.network)
8827       SendToServer_StopPlaying();
8828     else
8829 #endif
8830     {
8831       game_status = GAME_MODE_MAIN;
8832       DrawMainMenu();
8833     }
8834   }
8835   else
8836   {
8837     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8838   }
8839 }
8840
8841
8842 /* ---------- new game button stuff ---------------------------------------- */
8843
8844 /* graphic position values for game buttons */
8845 #define GAME_BUTTON_XSIZE       30
8846 #define GAME_BUTTON_YSIZE       30
8847 #define GAME_BUTTON_XPOS        5
8848 #define GAME_BUTTON_YPOS        215
8849 #define SOUND_BUTTON_XPOS       5
8850 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8851
8852 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8853 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8854 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8855 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8856 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8857 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8858
8859 static struct
8860 {
8861   int x, y;
8862   int gadget_id;
8863   char *infotext;
8864 } gamebutton_info[NUM_GAME_BUTTONS] =
8865 {
8866   {
8867     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
8868     GAME_CTRL_ID_STOP,
8869     "stop game"
8870   },
8871   {
8872     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
8873     GAME_CTRL_ID_PAUSE,
8874     "pause game"
8875   },
8876   {
8877     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
8878     GAME_CTRL_ID_PLAY,
8879     "play game"
8880   },
8881   {
8882     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
8883     SOUND_CTRL_ID_MUSIC,
8884     "background music on/off"
8885   },
8886   {
8887     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
8888     SOUND_CTRL_ID_LOOPS,
8889     "sound loops on/off"
8890   },
8891   {
8892     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
8893     SOUND_CTRL_ID_SIMPLE,
8894     "normal sounds on/off"
8895   }
8896 };
8897
8898 void CreateGameButtons()
8899 {
8900   int i;
8901
8902   for (i=0; i<NUM_GAME_BUTTONS; i++)
8903   {
8904     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8905     struct GadgetInfo *gi;
8906     int button_type;
8907     boolean checked;
8908     unsigned long event_mask;
8909     int gd_xoffset, gd_yoffset;
8910     int gd_x1, gd_x2, gd_y1, gd_y2;
8911     int id = i;
8912
8913     gd_xoffset = gamebutton_info[i].x;
8914     gd_yoffset = gamebutton_info[i].y;
8915     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8916     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8917
8918     if (id == GAME_CTRL_ID_STOP ||
8919         id == GAME_CTRL_ID_PAUSE ||
8920         id == GAME_CTRL_ID_PLAY)
8921     {
8922       button_type = GD_TYPE_NORMAL_BUTTON;
8923       checked = FALSE;
8924       event_mask = GD_EVENT_RELEASED;
8925       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8926       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8927     }
8928     else
8929     {
8930       button_type = GD_TYPE_CHECK_BUTTON;
8931       checked =
8932         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8933          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8934          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8935       event_mask = GD_EVENT_PRESSED;
8936       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
8937       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8938     }
8939
8940     gi = CreateGadget(GDI_CUSTOM_ID, id,
8941                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
8942                       GDI_X, DX + gd_xoffset,
8943                       GDI_Y, DY + gd_yoffset,
8944                       GDI_WIDTH, GAME_BUTTON_XSIZE,
8945                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
8946                       GDI_TYPE, button_type,
8947                       GDI_STATE, GD_BUTTON_UNPRESSED,
8948                       GDI_CHECKED, checked,
8949                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8950                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8951                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8952                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8953                       GDI_EVENT_MASK, event_mask,
8954                       GDI_CALLBACK_ACTION, HandleGameButtons,
8955                       GDI_END);
8956
8957     if (gi == NULL)
8958       Error(ERR_EXIT, "cannot create gadget");
8959
8960     game_gadget[id] = gi;
8961   }
8962 }
8963
8964 void FreeGameButtons()
8965 {
8966   int i;
8967
8968   for (i=0; i<NUM_GAME_BUTTONS; i++)
8969     FreeGadget(game_gadget[i]);
8970 }
8971
8972 static void MapGameButtons()
8973 {
8974   int i;
8975
8976   for (i=0; i<NUM_GAME_BUTTONS; i++)
8977     MapGadget(game_gadget[i]);
8978 }
8979
8980 void UnmapGameButtons()
8981 {
8982   int i;
8983
8984   for (i=0; i<NUM_GAME_BUTTONS; i++)
8985     UnmapGadget(game_gadget[i]);
8986 }
8987
8988 static void HandleGameButtons(struct GadgetInfo *gi)
8989 {
8990   int id = gi->custom_id;
8991
8992   if (game_status != GAME_MODE_PLAYING)
8993     return;
8994
8995   switch (id)
8996   {
8997     case GAME_CTRL_ID_STOP:
8998       RequestQuitGame(TRUE);
8999       break;
9000
9001     case GAME_CTRL_ID_PAUSE:
9002       if (options.network)
9003       {
9004 #if defined(PLATFORM_UNIX)
9005         if (tape.pausing)
9006           SendToServer_ContinuePlaying();
9007         else
9008           SendToServer_PausePlaying();
9009 #endif
9010       }
9011       else
9012         TapeTogglePause(TAPE_TOGGLE_MANUAL);
9013       break;
9014
9015     case GAME_CTRL_ID_PLAY:
9016       if (tape.pausing)
9017       {
9018 #if defined(PLATFORM_UNIX)
9019         if (options.network)
9020           SendToServer_ContinuePlaying();
9021         else
9022 #endif
9023         {
9024           tape.pausing = FALSE;
9025           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
9026         }
9027       }
9028       break;
9029
9030     case SOUND_CTRL_ID_MUSIC:
9031       if (setup.sound_music)
9032       { 
9033         setup.sound_music = FALSE;
9034         FadeMusic();
9035       }
9036       else if (audio.music_available)
9037       { 
9038         setup.sound = setup.sound_music = TRUE;
9039
9040         SetAudioMode(setup.sound);
9041
9042         PlayLevelMusic();
9043       }
9044       break;
9045
9046     case SOUND_CTRL_ID_LOOPS:
9047       if (setup.sound_loops)
9048         setup.sound_loops = FALSE;
9049       else if (audio.loops_available)
9050       {
9051         setup.sound = setup.sound_loops = TRUE;
9052         SetAudioMode(setup.sound);
9053       }
9054       break;
9055
9056     case SOUND_CTRL_ID_SIMPLE:
9057       if (setup.sound_simple)
9058         setup.sound_simple = FALSE;
9059       else if (audio.sound_available)
9060       {
9061         setup.sound = setup.sound_simple = TRUE;
9062         SetAudioMode(setup.sound);
9063       }
9064       break;
9065
9066     default:
9067       break;
9068   }
9069 }