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