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