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