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