cfb239c97717b8c1f120ee7153eaf2240b04bc8a
[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     TestIfBadThingTouchesOtherBadThing(newx, newy);
4661   }
4662   else if (element == EL_PENGUIN)
4663     TestIfFriendTouchesBadThing(newx, newy);
4664
4665   if (CAN_FALL(element) && direction == MV_DOWN &&
4666       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4667     Impact(x, newy);
4668
4669 #if 1
4670   TestIfElementTouchesCustomElement(x, y);              /* for empty space */
4671 #endif
4672
4673 #if 0
4674   if (ChangePage[newx][newy] != -1)                     /* delayed change */
4675     ChangeElement(newx, newy, ChangePage[newx][newy]);
4676 #endif
4677
4678   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4679     CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4680                            CE_COLLISION, -1);
4681
4682   TestIfPlayerTouchesCustomElement(newx, newy);
4683   TestIfElementTouchesCustomElement(newx, newy);
4684 }
4685
4686 int AmoebeNachbarNr(int ax, int ay)
4687 {
4688   int i;
4689   int element = Feld[ax][ay];
4690   int group_nr = 0;
4691   static int xy[4][2] =
4692   {
4693     { 0, -1 },
4694     { -1, 0 },
4695     { +1, 0 },
4696     { 0, +1 }
4697   };
4698
4699   for (i=0; i<4; i++)
4700   {
4701     int x = ax + xy[i][0];
4702     int y = ay + xy[i][1];
4703
4704     if (!IN_LEV_FIELD(x, y))
4705       continue;
4706
4707     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4708       group_nr = AmoebaNr[x][y];
4709   }
4710
4711   return group_nr;
4712 }
4713
4714 void AmoebenVereinigen(int ax, int ay)
4715 {
4716   int i, x, y, xx, yy;
4717   int new_group_nr = AmoebaNr[ax][ay];
4718   static int xy[4][2] =
4719   {
4720     { 0, -1 },
4721     { -1, 0 },
4722     { +1, 0 },
4723     { 0, +1 }
4724   };
4725
4726   if (new_group_nr == 0)
4727     return;
4728
4729   for (i=0; i<4; i++)
4730   {
4731     x = ax + xy[i][0];
4732     y = ay + xy[i][1];
4733
4734     if (!IN_LEV_FIELD(x, y))
4735       continue;
4736
4737     if ((Feld[x][y] == EL_AMOEBA_FULL ||
4738          Feld[x][y] == EL_BD_AMOEBA ||
4739          Feld[x][y] == EL_AMOEBA_DEAD) &&
4740         AmoebaNr[x][y] != new_group_nr)
4741     {
4742       int old_group_nr = AmoebaNr[x][y];
4743
4744       if (old_group_nr == 0)
4745         return;
4746
4747       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4748       AmoebaCnt[old_group_nr] = 0;
4749       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4750       AmoebaCnt2[old_group_nr] = 0;
4751
4752       for (yy=0; yy<lev_fieldy; yy++)
4753       {
4754         for (xx=0; xx<lev_fieldx; xx++)
4755         {
4756           if (AmoebaNr[xx][yy] == old_group_nr)
4757             AmoebaNr[xx][yy] = new_group_nr;
4758         }
4759       }
4760     }
4761   }
4762 }
4763
4764 void AmoebeUmwandeln(int ax, int ay)
4765 {
4766   int i, x, y;
4767
4768   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4769   {
4770     int group_nr = AmoebaNr[ax][ay];
4771
4772 #ifdef DEBUG
4773     if (group_nr == 0)
4774     {
4775       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4776       printf("AmoebeUmwandeln(): This should never happen!\n");
4777       return;
4778     }
4779 #endif
4780
4781     for (y=0; y<lev_fieldy; y++)
4782     {
4783       for (x=0; x<lev_fieldx; x++)
4784       {
4785         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4786         {
4787           AmoebaNr[x][y] = 0;
4788           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4789         }
4790       }
4791     }
4792     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4793                             SND_AMOEBA_TURNING_TO_GEM :
4794                             SND_AMOEBA_TURNING_TO_ROCK));
4795     Bang(ax, ay);
4796   }
4797   else
4798   {
4799     static int xy[4][2] =
4800     {
4801       { 0, -1 },
4802       { -1, 0 },
4803       { +1, 0 },
4804       { 0, +1 }
4805     };
4806
4807     for (i=0; i<4; i++)
4808     {
4809       x = ax + xy[i][0];
4810       y = ay + xy[i][1];
4811
4812       if (!IN_LEV_FIELD(x, y))
4813         continue;
4814
4815       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4816       {
4817         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4818                               SND_AMOEBA_TURNING_TO_GEM :
4819                               SND_AMOEBA_TURNING_TO_ROCK));
4820         Bang(x, y);
4821       }
4822     }
4823   }
4824 }
4825
4826 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4827 {
4828   int x, y;
4829   int group_nr = AmoebaNr[ax][ay];
4830   boolean done = FALSE;
4831
4832 #ifdef DEBUG
4833   if (group_nr == 0)
4834   {
4835     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4836     printf("AmoebeUmwandelnBD(): This should never happen!\n");
4837     return;
4838   }
4839 #endif
4840
4841   for (y=0; y<lev_fieldy; y++)
4842   {
4843     for (x=0; x<lev_fieldx; x++)
4844     {
4845       if (AmoebaNr[x][y] == group_nr &&
4846           (Feld[x][y] == EL_AMOEBA_DEAD ||
4847            Feld[x][y] == EL_BD_AMOEBA ||
4848            Feld[x][y] == EL_AMOEBA_GROWING))
4849       {
4850         AmoebaNr[x][y] = 0;
4851         Feld[x][y] = new_element;
4852         InitField(x, y, FALSE);
4853         DrawLevelField(x, y);
4854         done = TRUE;
4855       }
4856     }
4857   }
4858
4859   if (done)
4860     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4861                             SND_BD_AMOEBA_TURNING_TO_ROCK :
4862                             SND_BD_AMOEBA_TURNING_TO_GEM));
4863 }
4864
4865 void AmoebeWaechst(int x, int y)
4866 {
4867   static unsigned long sound_delay = 0;
4868   static unsigned long sound_delay_value = 0;
4869
4870   if (!MovDelay[x][y])          /* start new growing cycle */
4871   {
4872     MovDelay[x][y] = 7;
4873
4874     if (DelayReached(&sound_delay, sound_delay_value))
4875     {
4876 #if 1
4877       PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4878 #else
4879       if (Store[x][y] == EL_BD_AMOEBA)
4880         PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4881       else
4882         PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4883 #endif
4884       sound_delay_value = 30;
4885     }
4886   }
4887
4888   if (MovDelay[x][y])           /* wait some time before growing bigger */
4889   {
4890     MovDelay[x][y]--;
4891     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4892     {
4893       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4894                                            6 - MovDelay[x][y]);
4895
4896       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4897     }
4898
4899     if (!MovDelay[x][y])
4900     {
4901       Feld[x][y] = Store[x][y];
4902       Store[x][y] = 0;
4903       DrawLevelField(x, y);
4904     }
4905   }
4906 }
4907
4908 void AmoebaDisappearing(int x, int y)
4909 {
4910   static unsigned long sound_delay = 0;
4911   static unsigned long sound_delay_value = 0;
4912
4913   if (!MovDelay[x][y])          /* start new shrinking cycle */
4914   {
4915     MovDelay[x][y] = 7;
4916
4917     if (DelayReached(&sound_delay, sound_delay_value))
4918       sound_delay_value = 30;
4919   }
4920
4921   if (MovDelay[x][y])           /* wait some time before shrinking */
4922   {
4923     MovDelay[x][y]--;
4924     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4925     {
4926       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4927                                            6 - MovDelay[x][y]);
4928
4929       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4930     }
4931
4932     if (!MovDelay[x][y])
4933     {
4934       Feld[x][y] = EL_EMPTY;
4935       DrawLevelField(x, y);
4936
4937       /* don't let mole enter this field in this cycle;
4938          (give priority to objects falling to this field from above) */
4939       Stop[x][y] = TRUE;
4940     }
4941   }
4942 }
4943
4944 void AmoebeAbleger(int ax, int ay)
4945 {
4946   int i;
4947   int element = Feld[ax][ay];
4948   int graphic = el2img(element);
4949   int newax = ax, neway = ay;
4950   static int xy[4][2] =
4951   {
4952     { 0, -1 },
4953     { -1, 0 },
4954     { +1, 0 },
4955     { 0, +1 }
4956   };
4957
4958   if (!level.amoeba_speed)
4959   {
4960     Feld[ax][ay] = EL_AMOEBA_DEAD;
4961     DrawLevelField(ax, ay);
4962     return;
4963   }
4964
4965   if (IS_ANIMATED(graphic))
4966     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4967
4968   if (!MovDelay[ax][ay])        /* start making new amoeba field */
4969     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4970
4971   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
4972   {
4973     MovDelay[ax][ay]--;
4974     if (MovDelay[ax][ay])
4975       return;
4976   }
4977
4978   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4979   {
4980     int start = RND(4);
4981     int x = ax + xy[start][0];
4982     int y = ay + xy[start][1];
4983
4984     if (!IN_LEV_FIELD(x, y))
4985       return;
4986
4987     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4988     if (IS_FREE(x, y) ||
4989         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4990     {
4991       newax = x;
4992       neway = y;
4993     }
4994
4995     if (newax == ax && neway == ay)
4996       return;
4997   }
4998   else                          /* normal or "filled" (BD style) amoeba */
4999   {
5000     int start = RND(4);
5001     boolean waiting_for_player = FALSE;
5002
5003     for (i=0; i<4; i++)
5004     {
5005       int j = (start + i) % 4;
5006       int x = ax + xy[j][0];
5007       int y = ay + xy[j][1];
5008
5009       if (!IN_LEV_FIELD(x, y))
5010         continue;
5011
5012       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5013       if (IS_FREE(x, y) ||
5014           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5015       {
5016         newax = x;
5017         neway = y;
5018         break;
5019       }
5020       else if (IS_PLAYER(x, y))
5021         waiting_for_player = TRUE;
5022     }
5023
5024     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5025     {
5026       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5027       {
5028         Feld[ax][ay] = EL_AMOEBA_DEAD;
5029         DrawLevelField(ax, ay);
5030         AmoebaCnt[AmoebaNr[ax][ay]]--;
5031
5032         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5033         {
5034           if (element == EL_AMOEBA_FULL)
5035             AmoebeUmwandeln(ax, ay);
5036           else if (element == EL_BD_AMOEBA)
5037             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5038         }
5039       }
5040       return;
5041     }
5042     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5043     {
5044       /* amoeba gets larger by growing in some direction */
5045
5046       int new_group_nr = AmoebaNr[ax][ay];
5047
5048 #ifdef DEBUG
5049   if (new_group_nr == 0)
5050   {
5051     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5052     printf("AmoebeAbleger(): This should never happen!\n");
5053     return;
5054   }
5055 #endif
5056
5057       AmoebaNr[newax][neway] = new_group_nr;
5058       AmoebaCnt[new_group_nr]++;
5059       AmoebaCnt2[new_group_nr]++;
5060
5061       /* if amoeba touches other amoeba(s) after growing, unify them */
5062       AmoebenVereinigen(newax, neway);
5063
5064       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5065       {
5066         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5067         return;
5068       }
5069     }
5070   }
5071
5072   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5073       (neway == lev_fieldy - 1 && newax != ax))
5074   {
5075     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5076     Store[newax][neway] = element;
5077   }
5078   else if (neway == ay)
5079   {
5080     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5081 #if 1
5082     PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5083 #else
5084     PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5085 #endif
5086   }
5087   else
5088   {
5089     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5090     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5091     Store[ax][ay] = EL_AMOEBA_DROP;
5092     ContinueMoving(ax, ay);
5093     return;
5094   }
5095
5096   DrawLevelField(newax, neway);
5097 }
5098
5099 void Life(int ax, int ay)
5100 {
5101   int x1, y1, x2, y2;
5102   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5103   int life_time = 40;
5104   int element = Feld[ax][ay];
5105   int graphic = el2img(element);
5106   boolean changed = FALSE;
5107
5108   if (IS_ANIMATED(graphic))
5109     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5110
5111   if (Stop[ax][ay])
5112     return;
5113
5114   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5115     MovDelay[ax][ay] = life_time;
5116
5117   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5118   {
5119     MovDelay[ax][ay]--;
5120     if (MovDelay[ax][ay])
5121       return;
5122   }
5123
5124   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5125   {
5126     int xx = ax+x1, yy = ay+y1;
5127     int nachbarn = 0;
5128
5129     if (!IN_LEV_FIELD(xx, yy))
5130       continue;
5131
5132     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5133     {
5134       int x = xx+x2, y = yy+y2;
5135
5136       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5137         continue;
5138
5139       if (((Feld[x][y] == element ||
5140             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5141            !Stop[x][y]) ||
5142           (IS_FREE(x, y) && Stop[x][y]))
5143         nachbarn++;
5144     }
5145
5146     if (xx == ax && yy == ay)           /* field in the middle */
5147     {
5148       if (nachbarn < life[0] || nachbarn > life[1])
5149       {
5150         Feld[xx][yy] = EL_EMPTY;
5151         if (!Stop[xx][yy])
5152           DrawLevelField(xx, yy);
5153         Stop[xx][yy] = TRUE;
5154         changed = TRUE;
5155       }
5156     }
5157     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5158     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5159     {                                   /* free border field */
5160       if (nachbarn >= life[2] && nachbarn <= life[3])
5161       {
5162         Feld[xx][yy] = element;
5163         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5164         if (!Stop[xx][yy])
5165           DrawLevelField(xx, yy);
5166         Stop[xx][yy] = TRUE;
5167         changed = TRUE;
5168       }
5169     }
5170   }
5171
5172   if (changed)
5173     PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5174                    SND_GAME_OF_LIFE_GROWING);
5175 }
5176
5177 static void InitRobotWheel(int x, int y)
5178 {
5179   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5180 }
5181
5182 static void RunRobotWheel(int x, int y)
5183 {
5184   PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5185 }
5186
5187 static void StopRobotWheel(int x, int y)
5188 {
5189   if (ZX == x && ZY == y)
5190     ZX = ZY = -1;
5191 }
5192
5193 static void InitTimegateWheel(int x, int y)
5194 {
5195   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5196 }
5197
5198 static void RunTimegateWheel(int x, int y)
5199 {
5200   PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5201 }
5202
5203 void CheckExit(int x, int y)
5204 {
5205   if (local_player->gems_still_needed > 0 ||
5206       local_player->sokobanfields_still_needed > 0 ||
5207       local_player->lights_still_needed > 0)
5208   {
5209     int element = Feld[x][y];
5210     int graphic = el2img(element);
5211
5212     if (IS_ANIMATED(graphic))
5213       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5214
5215     return;
5216   }
5217
5218   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5219     return;
5220
5221   Feld[x][y] = EL_EXIT_OPENING;
5222
5223   PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5224 }
5225
5226 void CheckExitSP(int x, int y)
5227 {
5228   if (local_player->gems_still_needed > 0)
5229   {
5230     int element = Feld[x][y];
5231     int graphic = el2img(element);
5232
5233     if (IS_ANIMATED(graphic))
5234       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5235
5236     return;
5237   }
5238
5239   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5240     return;
5241
5242   Feld[x][y] = EL_SP_EXIT_OPENING;
5243
5244   PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5245 }
5246
5247 static void CloseAllOpenTimegates()
5248 {
5249   int x, y;
5250
5251   for (y=0; y<lev_fieldy; y++)
5252   {
5253     for (x=0; x<lev_fieldx; x++)
5254     {
5255       int element = Feld[x][y];
5256
5257       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5258       {
5259         Feld[x][y] = EL_TIMEGATE_CLOSING;
5260 #if 1
5261         PlaySoundLevelAction(x, y, ACTION_CLOSING);
5262 #else
5263         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5264 #endif
5265       }
5266     }
5267   }
5268 }
5269
5270 void EdelsteinFunkeln(int x, int y)
5271 {
5272   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5273     return;
5274
5275   if (Feld[x][y] == EL_BD_DIAMOND)
5276     return;
5277
5278   if (MovDelay[x][y] == 0)      /* next animation frame */
5279     MovDelay[x][y] = 11 * !SimpleRND(500);
5280
5281   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
5282   {
5283     MovDelay[x][y]--;
5284
5285     if (setup.direct_draw && MovDelay[x][y])
5286       SetDrawtoField(DRAW_BUFFERED);
5287
5288     DrawLevelElementAnimation(x, y, Feld[x][y]);
5289
5290     if (MovDelay[x][y] != 0)
5291     {
5292       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5293                                            10 - MovDelay[x][y]);
5294
5295       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5296
5297       if (setup.direct_draw)
5298       {
5299         int dest_x, dest_y;
5300
5301         dest_x = FX + SCREENX(x) * TILEX;
5302         dest_y = FY + SCREENY(y) * TILEY;
5303
5304         BlitBitmap(drawto_field, window,
5305                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5306         SetDrawtoField(DRAW_DIRECT);
5307       }
5308     }
5309   }
5310 }
5311
5312 void MauerWaechst(int x, int y)
5313 {
5314   int delay = 6;
5315
5316   if (!MovDelay[x][y])          /* next animation frame */
5317     MovDelay[x][y] = 3 * delay;
5318
5319   if (MovDelay[x][y])           /* wait some time before next frame */
5320   {
5321     MovDelay[x][y]--;
5322
5323     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5324     {
5325       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5326       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5327
5328       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5329     }
5330
5331     if (!MovDelay[x][y])
5332     {
5333       if (MovDir[x][y] == MV_LEFT)
5334       {
5335         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5336           DrawLevelField(x - 1, y);
5337       }
5338       else if (MovDir[x][y] == MV_RIGHT)
5339       {
5340         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5341           DrawLevelField(x + 1, y);
5342       }
5343       else if (MovDir[x][y] == MV_UP)
5344       {
5345         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5346           DrawLevelField(x, y - 1);
5347       }
5348       else
5349       {
5350         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5351           DrawLevelField(x, y + 1);
5352       }
5353
5354       Feld[x][y] = Store[x][y];
5355       Store[x][y] = 0;
5356       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5357       DrawLevelField(x, y);
5358     }
5359   }
5360 }
5361
5362 void MauerAbleger(int ax, int ay)
5363 {
5364   int element = Feld[ax][ay];
5365   int graphic = el2img(element);
5366   boolean oben_frei = FALSE, unten_frei = FALSE;
5367   boolean links_frei = FALSE, rechts_frei = FALSE;
5368   boolean oben_massiv = FALSE, unten_massiv = FALSE;
5369   boolean links_massiv = FALSE, rechts_massiv = FALSE;
5370   boolean new_wall = FALSE;
5371
5372   if (IS_ANIMATED(graphic))
5373     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5374
5375   if (!MovDelay[ax][ay])        /* start building new wall */
5376     MovDelay[ax][ay] = 6;
5377
5378   if (MovDelay[ax][ay])         /* wait some time before building new wall */
5379   {
5380     MovDelay[ax][ay]--;
5381     if (MovDelay[ax][ay])
5382       return;
5383   }
5384
5385   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5386     oben_frei = TRUE;
5387   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5388     unten_frei = TRUE;
5389   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5390     links_frei = TRUE;
5391   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5392     rechts_frei = TRUE;
5393
5394   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5395       element == EL_EXPANDABLE_WALL_ANY)
5396   {
5397     if (oben_frei)
5398     {
5399       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5400       Store[ax][ay-1] = element;
5401       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5402       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5403         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5404                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5405       new_wall = TRUE;
5406     }
5407     if (unten_frei)
5408     {
5409       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5410       Store[ax][ay+1] = element;
5411       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5412       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5413         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5414                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5415       new_wall = TRUE;
5416     }
5417   }
5418
5419   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5420       element == EL_EXPANDABLE_WALL_ANY ||
5421       element == EL_EXPANDABLE_WALL)
5422   {
5423     if (links_frei)
5424     {
5425       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5426       Store[ax-1][ay] = element;
5427       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5428       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5429         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5430                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5431       new_wall = TRUE;
5432     }
5433
5434     if (rechts_frei)
5435     {
5436       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5437       Store[ax+1][ay] = element;
5438       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5439       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5440         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5441                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5442       new_wall = TRUE;
5443     }
5444   }
5445
5446   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5447     DrawLevelField(ax, ay);
5448
5449   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5450     oben_massiv = TRUE;
5451   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5452     unten_massiv = TRUE;
5453   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5454     links_massiv = TRUE;
5455   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5456     rechts_massiv = TRUE;
5457
5458   if (((oben_massiv && unten_massiv) ||
5459        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5460        element == EL_EXPANDABLE_WALL) &&
5461       ((links_massiv && rechts_massiv) ||
5462        element == EL_EXPANDABLE_WALL_VERTICAL))
5463     Feld[ax][ay] = EL_WALL;
5464
5465   if (new_wall)
5466 #if 1
5467     PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5468 #else
5469     PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5470 #endif
5471 }
5472
5473 void CheckForDragon(int x, int y)
5474 {
5475   int i, j;
5476   boolean dragon_found = FALSE;
5477   static int xy[4][2] =
5478   {
5479     { 0, -1 },
5480     { -1, 0 },
5481     { +1, 0 },
5482     { 0, +1 }
5483   };
5484
5485   for (i=0; i<4; i++)
5486   {
5487     for (j=0; j<4; j++)
5488     {
5489       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5490
5491       if (IN_LEV_FIELD(xx, yy) &&
5492           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5493       {
5494         if (Feld[xx][yy] == EL_DRAGON)
5495           dragon_found = TRUE;
5496       }
5497       else
5498         break;
5499     }
5500   }
5501
5502   if (!dragon_found)
5503   {
5504     for (i=0; i<4; i++)
5505     {
5506       for (j=0; j<3; j++)
5507       {
5508         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5509   
5510         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5511         {
5512           Feld[xx][yy] = EL_EMPTY;
5513           DrawLevelField(xx, yy);
5514         }
5515         else
5516           break;
5517       }
5518     }
5519   }
5520 }
5521
5522 static void InitBuggyBase(int x, int y)
5523 {
5524   int element = Feld[x][y];
5525   int activating_delay = FRAMES_PER_SECOND / 4;
5526
5527   ChangeDelay[x][y] =
5528     (element == EL_SP_BUGGY_BASE ?
5529      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5530      element == EL_SP_BUGGY_BASE_ACTIVATING ?
5531      activating_delay :
5532      element == EL_SP_BUGGY_BASE_ACTIVE ?
5533      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5534 }
5535
5536 static void WarnBuggyBase(int x, int y)
5537 {
5538   int i;
5539   static int xy[4][2] =
5540   {
5541     { 0, -1 },
5542     { -1, 0 },
5543     { +1, 0 },
5544     { 0, +1 }
5545   };
5546
5547   for (i=0; i<4; i++)
5548   {
5549     int xx = x + xy[i][0], yy = y + xy[i][1];
5550
5551     if (IS_PLAYER(xx, yy))
5552     {
5553       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5554
5555       break;
5556     }
5557   }
5558 }
5559
5560 static void InitTrap(int x, int y)
5561 {
5562   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5563 }
5564
5565 static void ActivateTrap(int x, int y)
5566 {
5567   PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5568 }
5569
5570 static void ChangeActiveTrap(int x, int y)
5571 {
5572   int graphic = IMG_TRAP_ACTIVE;
5573
5574   /* if new animation frame was drawn, correct crumbled sand border */
5575   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5576     DrawLevelFieldCrumbledSand(x, y);
5577 }
5578
5579 static void ChangeElementNowExt(int x, int y, int target_element)
5580 {
5581   /* check if element under player changes from accessible to unaccessible
5582      (needed for special case of dropping element which then changes) */
5583   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5584       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5585   {
5586     Bang(x, y);
5587     return;
5588   }
5589
5590   RemoveField(x, y);
5591   Feld[x][y] = target_element;
5592
5593   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5594
5595   ResetGfxAnimation(x, y);
5596   ResetRandomAnimationValue(x, y);
5597
5598   InitField(x, y, FALSE);
5599   if (CAN_MOVE(Feld[x][y]))
5600     InitMovDir(x, y);
5601
5602   DrawLevelField(x, y);
5603
5604   if (GFX_CRUMBLED(Feld[x][y]))
5605     DrawLevelFieldCrumbledSandNeighbours(x, y);
5606
5607   TestIfBadThingTouchesHero(x, y);
5608   TestIfPlayerTouchesCustomElement(x, y);
5609   TestIfElementTouchesCustomElement(x, y);
5610
5611   if (ELEM_IS_PLAYER(target_element))
5612     RelocatePlayer(x, y, target_element);
5613 }
5614
5615 static boolean ChangeElementNow(int x, int y, int element, int page)
5616 {
5617   struct ElementChangeInfo *change = &element_info[element].change_page[page];
5618
5619   /* always use default change event to prevent running into a loop */
5620   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5621     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5622
5623   /* do not change already changed elements with same change event */
5624 #if 0
5625   if (Changed[x][y] & ChangeEvent[x][y])
5626     return FALSE;
5627 #else
5628   if (Changed[x][y])
5629     return FALSE;
5630 #endif
5631
5632   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5633
5634   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5635
5636   if (change->explode)
5637   {
5638     Bang(x, y);
5639
5640     return TRUE;
5641   }
5642
5643   if (change->use_content)
5644   {
5645     boolean complete_change = TRUE;
5646     boolean can_change[3][3];
5647     int xx, yy;
5648
5649     for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5650     {
5651       boolean half_destructible;
5652       int ex = x + xx - 1;
5653       int ey = y + yy - 1;
5654       int e;
5655
5656       can_change[xx][yy] = TRUE;
5657
5658       if (ex == x && ey == y)   /* do not check changing element itself */
5659         continue;
5660
5661       if (change->content[xx][yy] == EL_EMPTY_SPACE)
5662       {
5663         can_change[xx][yy] = FALSE;     /* do not change empty borders */
5664
5665         continue;
5666       }
5667
5668       if (!IN_LEV_FIELD(ex, ey))
5669       {
5670         can_change[xx][yy] = FALSE;
5671         complete_change = FALSE;
5672
5673         continue;
5674       }
5675
5676       e = Feld[ex][ey];
5677
5678       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5679         e = MovingOrBlocked2Element(ex, ey);
5680
5681       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5682
5683       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
5684           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5685           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5686       {
5687         can_change[xx][yy] = FALSE;
5688         complete_change = FALSE;
5689       }
5690     }
5691
5692     if (!change->only_complete || complete_change)
5693     {
5694       boolean something_has_changed = FALSE;
5695
5696       if (change->only_complete && change->use_random_change &&
5697           RND(100) < change->random)
5698         return FALSE;
5699
5700       for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5701       {
5702         int ex = x + xx - 1;
5703         int ey = y + yy - 1;
5704
5705         if (can_change[xx][yy] && (!change->use_random_change ||
5706                                    RND(100) < change->random))
5707         {
5708           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5709             RemoveMovingField(ex, ey);
5710
5711           ChangeEvent[ex][ey] = ChangeEvent[x][y];
5712
5713           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5714
5715           something_has_changed = TRUE;
5716
5717           /* for symmetry reasons, freeze newly created border elements */
5718           if (ex != x || ey != y)
5719             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
5720         }
5721       }
5722
5723       if (something_has_changed)
5724         PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5725     }
5726   }
5727   else
5728   {
5729     ChangeElementNowExt(x, y, change->target_element);
5730
5731     PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5732   }
5733
5734   return TRUE;
5735 }
5736
5737 static void ChangeElement(int x, int y, int page)
5738 {
5739   int element = MovingOrBlocked2Element(x, y);
5740   struct ElementInfo *ei = &element_info[element];
5741   struct ElementChangeInfo *change = &ei->change_page[page];
5742
5743 #if 0
5744 #ifdef DEBUG
5745   if (!CAN_CHANGE(element))
5746   {
5747     printf("\n\n");
5748     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5749            x, y, element, element_info[element].token_name);
5750     printf("ChangeElement(): This should never happen!\n");
5751     printf("\n\n");
5752   }
5753 #endif
5754 #endif
5755
5756   if (ChangeDelay[x][y] == 0)           /* initialize element change */
5757   {
5758     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
5759                          RND(change->delay_random * change->delay_frames)) + 1;
5760
5761     ResetGfxAnimation(x, y);
5762     ResetRandomAnimationValue(x, y);
5763
5764     if (change->pre_change_function)
5765       change->pre_change_function(x, y);
5766   }
5767
5768   ChangeDelay[x][y]--;
5769
5770   if (ChangeDelay[x][y] != 0)           /* continue element change */
5771   {
5772     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5773
5774     if (IS_ANIMATED(graphic))
5775       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5776
5777     if (change->change_function)
5778       change->change_function(x, y);
5779   }
5780   else                                  /* finish element change */
5781   {
5782     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
5783     {
5784       page = ChangePage[x][y];
5785       ChangePage[x][y] = -1;
5786     }
5787
5788     if (IS_MOVING(x, y))                /* never change a running system ;-) */
5789     {
5790       ChangeDelay[x][y] = 1;            /* try change after next move step */
5791       ChangePage[x][y] = page;          /* remember page to use for change */
5792
5793       return;
5794     }
5795
5796     if (ChangeElementNow(x, y, element, page))
5797     {
5798       if (change->post_change_function)
5799         change->post_change_function(x, y);
5800     }
5801   }
5802 }
5803
5804 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5805                                                int trigger_element,
5806                                                int trigger_side,
5807                                                int trigger_event)
5808 {
5809   int i, j, x, y;
5810
5811   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5812     return FALSE;
5813
5814   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5815   {
5816     int element = EL_CUSTOM_START + i;
5817
5818     boolean change_element = FALSE;
5819     int page = 0;
5820
5821     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5822       continue;
5823
5824     for (j=0; j < element_info[element].num_change_pages; j++)
5825     {
5826       struct ElementChangeInfo *change = &element_info[element].change_page[j];
5827
5828       if (change->can_change &&
5829 #if 1
5830           change->events & CH_EVENT_BIT(trigger_event) &&
5831 #endif
5832           change->sides & trigger_side &&
5833           change->trigger_element == trigger_element)
5834       {
5835 #if 0
5836         if (!(change->events & CH_EVENT_BIT(trigger_event)))
5837           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5838                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5839 #endif
5840
5841         change_element = TRUE;
5842         page = j;
5843
5844         break;
5845       }
5846     }
5847
5848     if (!change_element)
5849       continue;
5850
5851     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5852     {
5853 #if 0
5854       if (x == lx && y == ly)   /* do not change trigger element itself */
5855         continue;
5856 #endif
5857
5858       if (Feld[x][y] == element)
5859       {
5860         ChangeDelay[x][y] = 1;
5861         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5862         ChangeElement(x, y, page);
5863       }
5864     }
5865   }
5866
5867   return TRUE;
5868 }
5869
5870 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5871                                            int trigger_event)
5872 {
5873   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5874                                          trigger_event);
5875 }
5876
5877 static boolean CheckElementSideChange(int x, int y, int element, int side,
5878                                       int trigger_event, int page)
5879 {
5880   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5881     return FALSE;
5882
5883   if (Feld[x][y] == EL_BLOCKED)
5884   {
5885     Blocked2Moving(x, y, &x, &y);
5886     element = Feld[x][y];
5887   }
5888
5889   if (page < 0)
5890     page = element_info[element].event_page_nr[trigger_event];
5891
5892   if (!(element_info[element].change_page[page].sides & side))
5893     return FALSE;
5894
5895   ChangeDelay[x][y] = 1;
5896   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5897   ChangeElement(x, y, page);
5898
5899   return TRUE;
5900 }
5901
5902 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5903 {
5904   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5905 }
5906
5907 #if 1
5908 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
5909 {
5910 #if 0
5911   static byte stored_player_action[MAX_PLAYERS];
5912   static int num_stored_actions = 0;
5913 #endif
5914   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5915   int left      = player_action & JOY_LEFT;
5916   int right     = player_action & JOY_RIGHT;
5917   int up        = player_action & JOY_UP;
5918   int down      = player_action & JOY_DOWN;
5919   int button1   = player_action & JOY_BUTTON_1;
5920   int button2   = player_action & JOY_BUTTON_2;
5921   int dx        = (left ? -1    : right ? 1     : 0);
5922   int dy        = (up   ? -1    : down  ? 1     : 0);
5923
5924 #if 0
5925   stored_player_action[player->index_nr] = 0;
5926   num_stored_actions++;
5927 #endif
5928
5929 #if 0
5930   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
5931 #endif
5932
5933   if (!player->active || tape.pausing)
5934     return 0;
5935
5936   if (player_action)
5937   {
5938 #if 0
5939     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
5940 #endif
5941
5942     if (button1)
5943       snapped = SnapField(player, dx, dy);
5944     else
5945     {
5946       if (button2)
5947         dropped = DropElement(player);
5948
5949       moved = MovePlayer(player, dx, dy);
5950     }
5951
5952     if (tape.single_step && tape.recording && !tape.pausing)
5953     {
5954       if (button1 || (dropped && !moved))
5955       {
5956         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5957         SnapField(player, 0, 0);                /* stop snapping */
5958       }
5959     }
5960
5961 #if 1
5962     return player_action;
5963 #else
5964     stored_player_action[player->index_nr] = player_action;
5965 #endif
5966   }
5967   else
5968   {
5969 #if 0
5970     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
5971 #endif
5972
5973     /* no actions for this player (no input at player's configured device) */
5974
5975     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5976     SnapField(player, 0, 0);
5977     CheckGravityMovement(player);
5978
5979     if (player->MovPos == 0)
5980       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5981
5982     if (player->MovPos == 0)    /* needed for tape.playing */
5983       player->is_moving = FALSE;
5984
5985     return 0;
5986   }
5987
5988 #if 0
5989   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5990   {
5991     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
5992
5993     TapeRecordAction(stored_player_action);
5994     num_stored_actions = 0;
5995   }
5996 #endif
5997 }
5998
5999 #else
6000
6001 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6002 {
6003   static byte stored_player_action[MAX_PLAYERS];
6004   static int num_stored_actions = 0;
6005   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6006   int left      = player_action & JOY_LEFT;
6007   int right     = player_action & JOY_RIGHT;
6008   int up        = player_action & JOY_UP;
6009   int down      = player_action & JOY_DOWN;
6010   int button1   = player_action & JOY_BUTTON_1;
6011   int button2   = player_action & JOY_BUTTON_2;
6012   int dx        = (left ? -1    : right ? 1     : 0);
6013   int dy        = (up   ? -1    : down  ? 1     : 0);
6014
6015   stored_player_action[player->index_nr] = 0;
6016   num_stored_actions++;
6017
6018   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6019
6020   if (!player->active || tape.pausing)
6021     return;
6022
6023   if (player_action)
6024   {
6025     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6026
6027     if (button1)
6028       snapped = SnapField(player, dx, dy);
6029     else
6030     {
6031       if (button2)
6032         dropped = DropElement(player);
6033
6034       moved = MovePlayer(player, dx, dy);
6035     }
6036
6037     if (tape.single_step && tape.recording && !tape.pausing)
6038     {
6039       if (button1 || (dropped && !moved))
6040       {
6041         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6042         SnapField(player, 0, 0);                /* stop snapping */
6043       }
6044     }
6045
6046     stored_player_action[player->index_nr] = player_action;
6047   }
6048   else
6049   {
6050     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6051
6052     /* no actions for this player (no input at player's configured device) */
6053
6054     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6055     SnapField(player, 0, 0);
6056     CheckGravityMovement(player);
6057
6058     if (player->MovPos == 0)
6059       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6060
6061     if (player->MovPos == 0)    /* needed for tape.playing */
6062       player->is_moving = FALSE;
6063   }
6064
6065   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6066   {
6067     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6068
6069     TapeRecordAction(stored_player_action);
6070     num_stored_actions = 0;
6071   }
6072 }
6073 #endif
6074
6075 void GameActions()
6076 {
6077   static unsigned long action_delay = 0;
6078   unsigned long action_delay_value;
6079   int magic_wall_x = 0, magic_wall_y = 0;
6080   int i, x, y, element, graphic;
6081   byte *recorded_player_action;
6082   byte summarized_player_action = 0;
6083 #if 1
6084   byte tape_action[MAX_PLAYERS];
6085 #endif
6086
6087   if (game_status != GAME_MODE_PLAYING)
6088     return;
6089
6090   action_delay_value =
6091     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
6092
6093   if (tape.playing && tape.index_search && !tape.pausing)
6094     action_delay_value = 0;
6095
6096   /* ---------- main game synchronization point ---------- */
6097
6098   WaitUntilDelayReached(&action_delay, action_delay_value);
6099
6100   if (network_playing && !network_player_action_received)
6101   {
6102     /*
6103 #ifdef DEBUG
6104     printf("DEBUG: try to get network player actions in time\n");
6105 #endif
6106     */
6107
6108 #if defined(PLATFORM_UNIX)
6109     /* last chance to get network player actions without main loop delay */
6110     HandleNetworking();
6111 #endif
6112
6113     if (game_status != GAME_MODE_PLAYING)
6114       return;
6115
6116     if (!network_player_action_received)
6117     {
6118       /*
6119 #ifdef DEBUG
6120       printf("DEBUG: failed to get network player actions in time\n");
6121 #endif
6122       */
6123       return;
6124     }
6125   }
6126
6127   if (tape.pausing)
6128     return;
6129
6130   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6131
6132   for (i=0; i<MAX_PLAYERS; i++)
6133   {
6134     summarized_player_action |= stored_player[i].action;
6135
6136     if (!network_playing)
6137       stored_player[i].effective_action = stored_player[i].action;
6138   }
6139
6140 #if defined(PLATFORM_UNIX)
6141   if (network_playing)
6142     SendToServer_MovePlayer(summarized_player_action);
6143 #endif
6144
6145   if (!options.network && !setup.team_mode)
6146     local_player->effective_action = summarized_player_action;
6147
6148   for (i=0; i < MAX_PLAYERS; i++)
6149   {
6150     int actual_player_action = stored_player[i].effective_action;
6151
6152     if (stored_player[i].programmed_action)
6153       actual_player_action = stored_player[i].programmed_action;
6154
6155     if (recorded_player_action)
6156       actual_player_action = recorded_player_action[i];
6157
6158     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
6159
6160     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6161   }
6162
6163 #if 1
6164   if (tape.recording)
6165     TapeRecordAction(tape_action);
6166 #endif
6167
6168   network_player_action_received = FALSE;
6169
6170   ScrollScreen(NULL, SCROLL_GO_ON);
6171
6172 #if 0
6173   FrameCounter++;
6174   TimeFrames++;
6175
6176   for (i=0; i<MAX_PLAYERS; i++)
6177     stored_player[i].Frame++;
6178 #endif
6179
6180 #if 1
6181   if (game.engine_version < VERSION_IDENT(2,2,0,7))
6182   {
6183     for (i=0; i<MAX_PLAYERS; i++)
6184     {
6185       struct PlayerInfo *player = &stored_player[i];
6186       int x = player->jx;
6187       int y = player->jy;
6188
6189       if (player->active && player->is_pushing && player->is_moving &&
6190           IS_MOVING(x, y))
6191       {
6192         ContinueMoving(x, y);
6193
6194         /* continue moving after pushing (this is actually a bug) */
6195         if (!IS_MOVING(x, y))
6196         {
6197           Stop[x][y] = FALSE;
6198         }
6199       }
6200     }
6201   }
6202 #endif
6203
6204   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6205   {
6206     Changed[x][y] = CE_BITMASK_DEFAULT;
6207     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6208
6209 #if DEBUG
6210     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6211     {
6212       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6213       printf("GameActions(): This should never happen!\n");
6214
6215       ChangePage[x][y] = -1;
6216     }
6217 #endif
6218
6219     Stop[x][y] = FALSE;
6220     if (WasJustMoving[x][y] > 0)
6221       WasJustMoving[x][y]--;
6222     if (WasJustFalling[x][y] > 0)
6223       WasJustFalling[x][y]--;
6224
6225     GfxFrame[x][y]++;
6226
6227 #if 1
6228     /* reset finished pushing action (not done in ContinueMoving() to allow
6229        continous pushing animation for elements with zero push delay) */
6230     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6231     {
6232       ResetGfxAnimation(x, y);
6233       DrawLevelField(x, y);
6234     }
6235 #endif
6236
6237 #if DEBUG
6238     if (IS_BLOCKED(x, y))
6239     {
6240       int oldx, oldy;
6241
6242       Blocked2Moving(x, y, &oldx, &oldy);
6243       if (!IS_MOVING(oldx, oldy))
6244       {
6245         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6246         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6247         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6248         printf("GameActions(): This should never happen!\n");
6249       }
6250     }
6251 #endif
6252   }
6253
6254   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6255   {
6256     element = Feld[x][y];
6257 #if 1
6258     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6259 #else
6260     graphic = el2img(element);
6261 #endif
6262
6263 #if 0
6264     if (element == -1)
6265     {
6266       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6267
6268       element = graphic = 0;
6269     }
6270 #endif
6271
6272     if (graphic_info[graphic].anim_global_sync)
6273       GfxFrame[x][y] = FrameCounter;
6274
6275     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6276         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6277       ResetRandomAnimationValue(x, y);
6278
6279     SetRandomAnimationValue(x, y);
6280
6281 #if 1
6282     PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6283 #endif
6284
6285     if (IS_INACTIVE(element))
6286     {
6287       if (IS_ANIMATED(graphic))
6288         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6289
6290       continue;
6291     }
6292
6293 #if 1
6294     /* this may take place after moving, so 'element' may have changed */
6295 #if 0
6296     if (IS_CHANGING(x, y))
6297 #else
6298     if (IS_CHANGING(x, y) &&
6299         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6300 #endif
6301     {
6302 #if 0
6303       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6304                     element_info[element].event_page_nr[CE_DELAY]);
6305 #else
6306       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6307 #endif
6308
6309       element = Feld[x][y];
6310       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6311     }
6312 #endif
6313
6314     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6315     {
6316       StartMoving(x, y);
6317
6318 #if 1
6319       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6320 #if 0
6321       if (element == EL_MOLE)
6322         printf("::: %d, %d, %d [%d]\n",
6323                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6324                GfxAction[x][y]);
6325 #endif
6326 #if 0
6327       if (element == EL_YAMYAM)
6328         printf("::: %d, %d, %d\n",
6329                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6330 #endif
6331 #endif
6332
6333       if (IS_ANIMATED(graphic) &&
6334           !IS_MOVING(x, y) &&
6335           !Stop[x][y])
6336       {
6337         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6338
6339 #if 0
6340         if (element == EL_BUG)
6341           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6342 #endif
6343
6344 #if 0
6345         if (element == EL_MOLE)
6346           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6347 #endif
6348       }
6349
6350       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6351         EdelsteinFunkeln(x, y);
6352     }
6353     else if ((element == EL_ACID ||
6354               element == EL_EXIT_OPEN ||
6355               element == EL_SP_EXIT_OPEN ||
6356               element == EL_SP_TERMINAL ||
6357               element == EL_SP_TERMINAL_ACTIVE ||
6358               element == EL_EXTRA_TIME ||
6359               element == EL_SHIELD_NORMAL ||
6360               element == EL_SHIELD_DEADLY) &&
6361              IS_ANIMATED(graphic))
6362       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6363     else if (IS_MOVING(x, y))
6364       ContinueMoving(x, y);
6365     else if (IS_ACTIVE_BOMB(element))
6366       CheckDynamite(x, y);
6367 #if 0
6368     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6369       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6370 #endif
6371     else if (element == EL_AMOEBA_GROWING)
6372       AmoebeWaechst(x, y);
6373     else if (element == EL_AMOEBA_SHRINKING)
6374       AmoebaDisappearing(x, y);
6375
6376 #if !USE_NEW_AMOEBA_CODE
6377     else if (IS_AMOEBALIVE(element))
6378       AmoebeAbleger(x, y);
6379 #endif
6380
6381     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6382       Life(x, y);
6383     else if (element == EL_EXIT_CLOSED)
6384       CheckExit(x, y);
6385     else if (element == EL_SP_EXIT_CLOSED)
6386       CheckExitSP(x, y);
6387     else if (element == EL_EXPANDABLE_WALL_GROWING)
6388       MauerWaechst(x, y);
6389     else if (element == EL_EXPANDABLE_WALL ||
6390              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6391              element == EL_EXPANDABLE_WALL_VERTICAL ||
6392              element == EL_EXPANDABLE_WALL_ANY)
6393       MauerAbleger(x, y);
6394     else if (element == EL_FLAMES)
6395       CheckForDragon(x, y);
6396 #if 0
6397     else if (IS_AUTO_CHANGING(element))
6398       ChangeElement(x, y);
6399 #endif
6400     else if (element == EL_EXPLOSION)
6401       ; /* drawing of correct explosion animation is handled separately */
6402     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6403       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6404
6405 #if 0
6406     /* this may take place after moving, so 'element' may have changed */
6407     if (IS_AUTO_CHANGING(Feld[x][y]))
6408       ChangeElement(x, y);
6409 #endif
6410
6411     if (IS_BELT_ACTIVE(element))
6412       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6413
6414     if (game.magic_wall_active)
6415     {
6416       int jx = local_player->jx, jy = local_player->jy;
6417
6418       /* play the element sound at the position nearest to the player */
6419       if ((element == EL_MAGIC_WALL_FULL ||
6420            element == EL_MAGIC_WALL_ACTIVE ||
6421            element == EL_MAGIC_WALL_EMPTYING ||
6422            element == EL_BD_MAGIC_WALL_FULL ||
6423            element == EL_BD_MAGIC_WALL_ACTIVE ||
6424            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6425           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6426       {
6427         magic_wall_x = x;
6428         magic_wall_y = y;
6429       }
6430     }
6431   }
6432
6433 #if USE_NEW_AMOEBA_CODE
6434   /* new experimental amoeba growth stuff */
6435 #if 1
6436   if (!(FrameCounter % 8))
6437 #endif
6438   {
6439     static unsigned long random = 1684108901;
6440
6441     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6442     {
6443 #if 0
6444       x = (random >> 10) % lev_fieldx;
6445       y = (random >> 20) % lev_fieldy;
6446 #else
6447       x = RND(lev_fieldx);
6448       y = RND(lev_fieldy);
6449 #endif
6450       element = Feld[x][y];
6451
6452       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6453       if (!IS_PLAYER(x,y) &&
6454           (element == EL_EMPTY ||
6455            element == EL_SAND ||
6456            element == EL_QUICKSAND_EMPTY ||
6457            element == EL_ACID_SPLASH_LEFT ||
6458            element == EL_ACID_SPLASH_RIGHT))
6459       {
6460         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6461             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6462             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6463             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6464           Feld[x][y] = EL_AMOEBA_DROP;
6465       }
6466
6467       random = random * 129 + 1;
6468     }
6469   }
6470 #endif
6471
6472 #if 0
6473   if (game.explosions_delayed)
6474 #endif
6475   {
6476     game.explosions_delayed = FALSE;
6477
6478     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6479     {
6480       element = Feld[x][y];
6481
6482       if (ExplodeField[x][y])
6483         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6484       else if (element == EL_EXPLOSION)
6485         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6486
6487       ExplodeField[x][y] = EX_NO_EXPLOSION;
6488     }
6489
6490     game.explosions_delayed = TRUE;
6491   }
6492
6493   if (game.magic_wall_active)
6494   {
6495     if (!(game.magic_wall_time_left % 4))
6496     {
6497       int element = Feld[magic_wall_x][magic_wall_y];
6498
6499       if (element == EL_BD_MAGIC_WALL_FULL ||
6500           element == EL_BD_MAGIC_WALL_ACTIVE ||
6501           element == EL_BD_MAGIC_WALL_EMPTYING)
6502         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6503       else
6504         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6505     }
6506
6507     if (game.magic_wall_time_left > 0)
6508     {
6509       game.magic_wall_time_left--;
6510       if (!game.magic_wall_time_left)
6511       {
6512         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6513         {
6514           element = Feld[x][y];
6515
6516           if (element == EL_MAGIC_WALL_ACTIVE ||
6517               element == EL_MAGIC_WALL_FULL)
6518           {
6519             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6520             DrawLevelField(x, y);
6521           }
6522           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6523                    element == EL_BD_MAGIC_WALL_FULL)
6524           {
6525             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6526             DrawLevelField(x, y);
6527           }
6528         }
6529
6530         game.magic_wall_active = FALSE;
6531       }
6532     }
6533   }
6534
6535   if (game.light_time_left > 0)
6536   {
6537     game.light_time_left--;
6538
6539     if (game.light_time_left == 0)
6540       RedrawAllLightSwitchesAndInvisibleElements();
6541   }
6542
6543   if (game.timegate_time_left > 0)
6544   {
6545     game.timegate_time_left--;
6546
6547     if (game.timegate_time_left == 0)
6548       CloseAllOpenTimegates();
6549   }
6550
6551   for (i=0; i<MAX_PLAYERS; i++)
6552   {
6553     struct PlayerInfo *player = &stored_player[i];
6554
6555     if (SHIELD_ON(player))
6556     {
6557       if (player->shield_deadly_time_left)
6558         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6559       else if (player->shield_normal_time_left)
6560         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6561     }
6562   }
6563
6564   if (TimeFrames >= FRAMES_PER_SECOND)
6565   {
6566     TimeFrames = 0;
6567     TimePlayed++;
6568
6569     for (i=0; i<MAX_PLAYERS; i++)
6570     {
6571       struct PlayerInfo *player = &stored_player[i];
6572
6573       if (SHIELD_ON(player))
6574       {
6575         player->shield_normal_time_left--;
6576
6577         if (player->shield_deadly_time_left > 0)
6578           player->shield_deadly_time_left--;
6579       }
6580     }
6581
6582     if (tape.recording || tape.playing)
6583       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6584
6585     if (TimeLeft > 0)
6586     {
6587       TimeLeft--;
6588
6589       if (TimeLeft <= 10 && setup.time_limit)
6590         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6591
6592       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6593
6594       if (!TimeLeft && setup.time_limit)
6595         for (i=0; i<MAX_PLAYERS; i++)
6596           KillHero(&stored_player[i]);
6597     }
6598     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6599       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6600   }
6601
6602   DrawAllPlayers();
6603
6604   if (options.debug)                    /* calculate frames per second */
6605   {
6606     static unsigned long fps_counter = 0;
6607     static int fps_frames = 0;
6608     unsigned long fps_delay_ms = Counter() - fps_counter;
6609
6610     fps_frames++;
6611
6612     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
6613     {
6614       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6615
6616       fps_frames = 0;
6617       fps_counter = Counter();
6618     }
6619
6620     redraw_mask |= REDRAW_FPS;
6621   }
6622
6623 #if 0
6624   if (stored_player[0].jx != stored_player[0].last_jx ||
6625       stored_player[0].jy != stored_player[0].last_jy)
6626     printf("::: %d, %d, %d, %d, %d\n",
6627            stored_player[0].MovDir,
6628            stored_player[0].MovPos,
6629            stored_player[0].GfxPos,
6630            stored_player[0].Frame,
6631            stored_player[0].StepFrame);
6632 #endif
6633
6634 #if 1
6635   FrameCounter++;
6636   TimeFrames++;
6637
6638   for (i=0; i<MAX_PLAYERS; i++)
6639   {
6640     int move_frames =
6641       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
6642
6643     stored_player[i].Frame += move_frames;
6644
6645     if (stored_player[i].MovPos != 0)
6646       stored_player[i].StepFrame += move_frames;
6647   }
6648 #endif
6649
6650 #if 1
6651   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6652   {
6653     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6654
6655     local_player->show_envelope = 0;
6656   }
6657 #endif
6658 }
6659
6660 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6661 {
6662   int min_x = x, min_y = y, max_x = x, max_y = y;
6663   int i;
6664
6665   for (i=0; i<MAX_PLAYERS; i++)
6666   {
6667     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6668
6669     if (!stored_player[i].active || &stored_player[i] == player)
6670       continue;
6671
6672     min_x = MIN(min_x, jx);
6673     min_y = MIN(min_y, jy);
6674     max_x = MAX(max_x, jx);
6675     max_y = MAX(max_y, jy);
6676   }
6677
6678   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6679 }
6680
6681 static boolean AllPlayersInVisibleScreen()
6682 {
6683   int i;
6684
6685   for (i=0; i<MAX_PLAYERS; i++)
6686   {
6687     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6688
6689     if (!stored_player[i].active)
6690       continue;
6691
6692     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6693       return FALSE;
6694   }
6695
6696   return TRUE;
6697 }
6698
6699 void ScrollLevel(int dx, int dy)
6700 {
6701   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6702   int x, y;
6703
6704   BlitBitmap(drawto_field, drawto_field,
6705              FX + TILEX * (dx == -1) - softscroll_offset,
6706              FY + TILEY * (dy == -1) - softscroll_offset,
6707              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6708              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6709              FX + TILEX * (dx == 1) - softscroll_offset,
6710              FY + TILEY * (dy == 1) - softscroll_offset);
6711
6712   if (dx)
6713   {
6714     x = (dx == 1 ? BX1 : BX2);
6715     for (y=BY1; y <= BY2; y++)
6716       DrawScreenField(x, y);
6717   }
6718
6719   if (dy)
6720   {
6721     y = (dy == 1 ? BY1 : BY2);
6722     for (x=BX1; x <= BX2; x++)
6723       DrawScreenField(x, y);
6724   }
6725
6726   redraw_mask |= REDRAW_FIELD;
6727 }
6728
6729 static void CheckGravityMovement(struct PlayerInfo *player)
6730 {
6731   if (game.gravity && !player->programmed_action)
6732   {
6733     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6734     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6735     int move_dir =
6736       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6737        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6738        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6739     int jx = player->jx, jy = player->jy;
6740     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6741     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6742     int new_jx = jx + dx, new_jy = jy + dy;
6743     boolean field_under_player_is_free =
6744       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6745     boolean player_is_moving_to_valid_field =
6746       (IN_LEV_FIELD(new_jx, new_jy) &&
6747        (Feld[new_jx][new_jy] == EL_SP_BASE ||
6748         Feld[new_jx][new_jy] == EL_SAND));
6749     /* !!! extend EL_SAND to anything diggable !!! */
6750
6751     if (field_under_player_is_free &&
6752         !player_is_moving_to_valid_field &&
6753         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6754       player->programmed_action = MV_DOWN;
6755   }
6756 }
6757
6758 /*
6759   MovePlayerOneStep()
6760   -----------------------------------------------------------------------------
6761   dx, dy:               direction (non-diagonal) to try to move the player to
6762   real_dx, real_dy:     direction as read from input device (can be diagonal)
6763 */
6764
6765 boolean MovePlayerOneStep(struct PlayerInfo *player,
6766                           int dx, int dy, int real_dx, int real_dy)
6767 {
6768 #if 0
6769   static int change_sides[4][2] =
6770   {
6771     /* enter side        leave side */
6772     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
6773     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
6774     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
6775     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
6776   };
6777   int move_direction = (dx == -1 ? MV_LEFT :
6778                         dx == +1 ? MV_RIGHT :
6779                         dy == -1 ? MV_UP :
6780                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
6781   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6782   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6783 #endif
6784   int jx = player->jx, jy = player->jy;
6785   int new_jx = jx + dx, new_jy = jy + dy;
6786   int element;
6787   int can_move;
6788
6789   if (!player->active || (!dx && !dy))
6790     return MF_NO_ACTION;
6791
6792   player->MovDir = (dx < 0 ? MV_LEFT :
6793                     dx > 0 ? MV_RIGHT :
6794                     dy < 0 ? MV_UP :
6795                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6796
6797   if (!IN_LEV_FIELD(new_jx, new_jy))
6798     return MF_NO_ACTION;
6799
6800   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6801     return MF_NO_ACTION;
6802
6803 #if 0
6804   element = MovingOrBlocked2Element(new_jx, new_jy);
6805 #else
6806   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6807 #endif
6808
6809   if (DONT_RUN_INTO(element))
6810   {
6811     if (element == EL_ACID && dx == 0 && dy == 1)
6812     {
6813       SplashAcid(jx, jy);
6814       Feld[jx][jy] = EL_PLAYER_1;
6815       InitMovingField(jx, jy, MV_DOWN);
6816       Store[jx][jy] = EL_ACID;
6817       ContinueMoving(jx, jy);
6818       BuryHero(player);
6819     }
6820     else
6821       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6822
6823     return MF_MOVING;
6824   }
6825
6826   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6827   if (can_move != MF_MOVING)
6828     return can_move;
6829
6830   /* check if DigField() has caused relocation of the player */
6831   if (player->jx != jx || player->jy != jy)
6832     return MF_NO_ACTION;
6833
6834   StorePlayer[jx][jy] = 0;
6835   player->last_jx = jx;
6836   player->last_jy = jy;
6837   player->jx = new_jx;
6838   player->jy = new_jy;
6839   StorePlayer[new_jx][new_jy] = player->element_nr;
6840
6841   player->MovPos =
6842     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6843
6844   ScrollPlayer(player, SCROLL_INIT);
6845
6846 #if 0
6847   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6848   {
6849     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6850                                     CE_OTHER_GETS_LEFT);
6851     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6852                            CE_LEFT_BY_PLAYER, -1);
6853   }
6854
6855   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6856   {
6857     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6858                                     enter_side, CE_OTHER_GETS_ENTERED);
6859     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6860                            CE_ENTERED_BY_PLAYER, -1);
6861   }
6862 #endif
6863
6864   return MF_MOVING;
6865 }
6866
6867 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6868 {
6869   int jx = player->jx, jy = player->jy;
6870   int old_jx = jx, old_jy = jy;
6871   int moved = MF_NO_ACTION;
6872
6873   if (!player->active || (!dx && !dy))
6874     return FALSE;
6875
6876 #if 0
6877   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6878       !tape.playing)
6879     return FALSE;
6880 #else
6881   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6882       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6883     return FALSE;
6884 #endif
6885
6886   /* remove the last programmed player action */
6887   player->programmed_action = 0;
6888
6889   if (player->MovPos)
6890   {
6891     /* should only happen if pre-1.2 tape recordings are played */
6892     /* this is only for backward compatibility */
6893
6894     int original_move_delay_value = player->move_delay_value;
6895
6896 #if DEBUG
6897     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6898            tape.counter);
6899 #endif
6900
6901     /* scroll remaining steps with finest movement resolution */
6902     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6903
6904     while (player->MovPos)
6905     {
6906       ScrollPlayer(player, SCROLL_GO_ON);
6907       ScrollScreen(NULL, SCROLL_GO_ON);
6908       FrameCounter++;
6909       DrawAllPlayers();
6910       BackToFront();
6911     }
6912
6913     player->move_delay_value = original_move_delay_value;
6914   }
6915
6916   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6917   {
6918     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6919       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6920   }
6921   else
6922   {
6923     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6924       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6925   }
6926
6927   jx = player->jx;
6928   jy = player->jy;
6929
6930   if (moved & MF_MOVING && !ScreenMovPos &&
6931       (player == local_player || !options.network))
6932   {
6933     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6934     int offset = (setup.scroll_delay ? 3 : 0);
6935
6936     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6937     {
6938       /* actual player has left the screen -- scroll in that direction */
6939       if (jx != old_jx)         /* player has moved horizontally */
6940         scroll_x += (jx - old_jx);
6941       else                      /* player has moved vertically */
6942         scroll_y += (jy - old_jy);
6943     }
6944     else
6945     {
6946       if (jx != old_jx)         /* player has moved horizontally */
6947       {
6948         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6949             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6950           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6951
6952         /* don't scroll over playfield boundaries */
6953         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6954           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6955
6956         /* don't scroll more than one field at a time */
6957         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6958
6959         /* don't scroll against the player's moving direction */
6960         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6961             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6962           scroll_x = old_scroll_x;
6963       }
6964       else                      /* player has moved vertically */
6965       {
6966         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6967             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6968           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6969
6970         /* don't scroll over playfield boundaries */
6971         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6972           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6973
6974         /* don't scroll more than one field at a time */
6975         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6976
6977         /* don't scroll against the player's moving direction */
6978         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6979             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6980           scroll_y = old_scroll_y;
6981       }
6982     }
6983
6984     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6985     {
6986       if (!options.network && !AllPlayersInVisibleScreen())
6987       {
6988         scroll_x = old_scroll_x;
6989         scroll_y = old_scroll_y;
6990       }
6991       else
6992       {
6993         ScrollScreen(player, SCROLL_INIT);
6994         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6995       }
6996     }
6997   }
6998
6999 #if 0
7000 #if 1
7001   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7002 #else
7003   if (!(moved & MF_MOVING) && !player->is_pushing)
7004     player->Frame = 0;
7005 #endif
7006 #endif
7007
7008   player->StepFrame = 0;
7009
7010   if (moved & MF_MOVING)
7011   {
7012     if (old_jx != jx && old_jy == jy)
7013       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
7014     else if (old_jx == jx && old_jy != jy)
7015       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
7016
7017     DrawLevelField(jx, jy);     /* for "crumbled sand" */
7018
7019     player->last_move_dir = player->MovDir;
7020     player->is_moving = TRUE;
7021 #if 1
7022     player->is_snapping = FALSE;
7023 #endif
7024
7025 #if 1
7026     player->is_switching = FALSE;
7027 #endif
7028
7029
7030 #if 1
7031     {
7032       static int change_sides[4][2] =
7033       {
7034         /* enter side           leave side */
7035         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
7036         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
7037         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
7038         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
7039       };
7040       int move_direction = player->MovDir;
7041       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7042       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7043
7044 #if 1
7045       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
7046       {
7047         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7048                                         leave_side, CE_OTHER_GETS_LEFT);
7049         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
7050                                leave_side, CE_LEFT_BY_PLAYER, -1);
7051       }
7052
7053       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7054       {
7055         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
7056                                         enter_side, CE_OTHER_GETS_ENTERED);
7057         CheckElementSideChange(jx, jy, Feld[jx][jy],
7058                                enter_side, CE_ENTERED_BY_PLAYER, -1);
7059       }
7060 #endif
7061
7062     }
7063 #endif
7064
7065
7066   }
7067   else
7068   {
7069     CheckGravityMovement(player);
7070
7071     /*
7072     player->last_move_dir = MV_NO_MOVING;
7073     */
7074     player->is_moving = FALSE;
7075   }
7076
7077   if (game.engine_version < VERSION_IDENT(3,0,7,0))
7078   {
7079     TestIfHeroTouchesBadThing(jx, jy);
7080     TestIfPlayerTouchesCustomElement(jx, jy);
7081   }
7082
7083   if (!player->active)
7084     RemoveHero(player);
7085
7086   return moved;
7087 }
7088
7089 void ScrollPlayer(struct PlayerInfo *player, int mode)
7090 {
7091   int jx = player->jx, jy = player->jy;
7092   int last_jx = player->last_jx, last_jy = player->last_jy;
7093   int move_stepsize = TILEX / player->move_delay_value;
7094
7095   if (!player->active || !player->MovPos)
7096     return;
7097
7098   if (mode == SCROLL_INIT)
7099   {
7100     player->actual_frame_counter = FrameCounter;
7101     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7102
7103     if (Feld[last_jx][last_jy] == EL_EMPTY)
7104       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
7105
7106 #if 0
7107     DrawPlayer(player);
7108 #endif
7109     return;
7110   }
7111   else if (!FrameReached(&player->actual_frame_counter, 1))
7112     return;
7113
7114   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
7115   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7116
7117   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7118     Feld[last_jx][last_jy] = EL_EMPTY;
7119
7120   /* before DrawPlayer() to draw correct player graphic for this case */
7121   if (player->MovPos == 0)
7122     CheckGravityMovement(player);
7123
7124 #if 0
7125   DrawPlayer(player);   /* needed here only to cleanup last field */
7126 #endif
7127
7128   if (player->MovPos == 0)      /* player reached destination field */
7129   {
7130     if (IS_PASSABLE(Feld[last_jx][last_jy]))
7131     {
7132       /* continue with normal speed after quickly moving through gate */
7133       HALVE_PLAYER_SPEED(player);
7134
7135       /* be able to make the next move without delay */
7136       player->move_delay = 0;
7137     }
7138
7139     player->last_jx = jx;
7140     player->last_jy = jy;
7141
7142     if (Feld[jx][jy] == EL_EXIT_OPEN ||
7143         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7144         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
7145     {
7146       DrawPlayer(player);       /* needed here only to cleanup last field */
7147       RemoveHero(player);
7148
7149       if (local_player->friends_still_needed == 0 ||
7150           IS_SP_ELEMENT(Feld[jx][jy]))
7151         player->LevelSolved = player->GameOver = TRUE;
7152     }
7153
7154     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7155     {
7156       TestIfHeroTouchesBadThing(jx, jy);
7157       TestIfPlayerTouchesCustomElement(jx, jy);
7158 #if 1
7159       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
7160 #endif
7161
7162       if (!player->active)
7163         RemoveHero(player);
7164     }
7165
7166     if (tape.single_step && tape.recording && !tape.pausing &&
7167         !player->programmed_action)
7168       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7169   }
7170 }
7171
7172 void ScrollScreen(struct PlayerInfo *player, int mode)
7173 {
7174   static unsigned long screen_frame_counter = 0;
7175
7176   if (mode == SCROLL_INIT)
7177   {
7178     /* set scrolling step size according to actual player's moving speed */
7179     ScrollStepSize = TILEX / player->move_delay_value;
7180
7181     screen_frame_counter = FrameCounter;
7182     ScreenMovDir = player->MovDir;
7183     ScreenMovPos = player->MovPos;
7184     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7185     return;
7186   }
7187   else if (!FrameReached(&screen_frame_counter, 1))
7188     return;
7189
7190   if (ScreenMovPos)
7191   {
7192     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7193     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7194     redraw_mask |= REDRAW_FIELD;
7195   }
7196   else
7197     ScreenMovDir = MV_NO_MOVING;
7198 }
7199
7200 void TestIfPlayerTouchesCustomElement(int x, int y)
7201 {
7202   static int xy[4][2] =
7203   {
7204     { 0, -1 },
7205     { -1, 0 },
7206     { +1, 0 },
7207     { 0, +1 }
7208   };
7209   static int change_sides[4][2] =
7210   {
7211     /* center side       border side */
7212     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7213     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7214     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7215     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7216   };
7217   static int touch_dir[4] =
7218   {
7219     MV_LEFT | MV_RIGHT,
7220     MV_UP   | MV_DOWN,
7221     MV_UP   | MV_DOWN,
7222     MV_LEFT | MV_RIGHT
7223   };
7224   int center_element = Feld[x][y];      /* should always be non-moving! */
7225   int i;
7226
7227   for (i=0; i<4; i++)
7228   {
7229     int xx = x + xy[i][0];
7230     int yy = y + xy[i][1];
7231     int center_side = change_sides[i][0];
7232     int border_side = change_sides[i][1];
7233     int border_element;
7234
7235     if (!IN_LEV_FIELD(xx, yy))
7236       continue;
7237
7238     if (IS_PLAYER(x, y))
7239     {
7240       if (game.engine_version < VERSION_IDENT(3,0,7,0))
7241         border_element = Feld[xx][yy];          /* may be moving! */
7242       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7243         border_element = Feld[xx][yy];
7244       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7245         border_element = MovingOrBlocked2Element(xx, yy);
7246       else
7247         continue;               /* center and border element do not touch */
7248
7249       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7250                                       CE_OTHER_GETS_TOUCHED);
7251       CheckElementSideChange(xx, yy, border_element, border_side,
7252                              CE_TOUCHED_BY_PLAYER, -1);
7253     }
7254     else if (IS_PLAYER(xx, yy))
7255     {
7256       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7257       {
7258         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7259
7260         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7261           continue;             /* center and border element do not touch */
7262       }
7263
7264       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7265                                       CE_OTHER_GETS_TOUCHED);
7266       CheckElementSideChange(x, y, center_element, center_side,
7267                              CE_TOUCHED_BY_PLAYER, -1);
7268
7269       break;
7270     }
7271   }
7272 }
7273
7274 void TestIfElementTouchesCustomElement(int x, int y)
7275 {
7276   static int xy[4][2] =
7277   {
7278     { 0, -1 },
7279     { -1, 0 },
7280     { +1, 0 },
7281     { 0, +1 }
7282   };
7283   static int change_sides[4][2] =
7284   {
7285     /* center side       border side */
7286     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7287     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7288     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7289     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7290   };
7291   static int touch_dir[4] =
7292   {
7293     MV_LEFT | MV_RIGHT,
7294     MV_UP   | MV_DOWN,
7295     MV_UP   | MV_DOWN,
7296     MV_LEFT | MV_RIGHT
7297   };
7298   boolean change_center_element = FALSE;
7299   int center_element_change_page = 0;
7300   int center_element = Feld[x][y];      /* should always be non-moving! */
7301   int i, j;
7302
7303   for (i=0; i<4; i++)
7304   {
7305     int xx = x + xy[i][0];
7306     int yy = y + xy[i][1];
7307     int center_side = change_sides[i][0];
7308     int border_side = change_sides[i][1];
7309     int border_element;
7310
7311     if (!IN_LEV_FIELD(xx, yy))
7312       continue;
7313
7314     if (game.engine_version < VERSION_IDENT(3,0,7,0))
7315       border_element = Feld[xx][yy];    /* may be moving! */
7316     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7317       border_element = Feld[xx][yy];
7318     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7319       border_element = MovingOrBlocked2Element(xx, yy);
7320     else
7321       continue;                 /* center and border element do not touch */
7322
7323     /* check for change of center element (but change it only once) */
7324     if (IS_CUSTOM_ELEMENT(center_element) &&
7325         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7326         !change_center_element)
7327     {
7328       for (j=0; j < element_info[center_element].num_change_pages; j++)
7329       {
7330         struct ElementChangeInfo *change =
7331           &element_info[center_element].change_page[j];
7332
7333         if (change->can_change &&
7334             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7335             change->sides & border_side &&
7336             change->trigger_element == border_element)
7337         {
7338           change_center_element = TRUE;
7339           center_element_change_page = j;
7340
7341           break;
7342         }
7343       }
7344     }
7345
7346     /* check for change of border element */
7347     if (IS_CUSTOM_ELEMENT(border_element) &&
7348         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7349     {
7350       for (j=0; j < element_info[border_element].num_change_pages; j++)
7351       {
7352         struct ElementChangeInfo *change =
7353           &element_info[border_element].change_page[j];
7354
7355         if (change->can_change &&
7356             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7357             change->sides & center_side &&
7358             change->trigger_element == center_element)
7359         {
7360           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7361                                  CE_OTHER_IS_TOUCHING, j);
7362           break;
7363         }
7364       }
7365     }
7366   }
7367
7368   if (change_center_element)
7369     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7370                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7371 }
7372
7373 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7374 {
7375   int i, kill_x = -1, kill_y = -1;
7376   static int test_xy[4][2] =
7377   {
7378     { 0, -1 },
7379     { -1, 0 },
7380     { +1, 0 },
7381     { 0, +1 }
7382   };
7383   static int test_dir[4] =
7384   {
7385     MV_UP,
7386     MV_LEFT,
7387     MV_RIGHT,
7388     MV_DOWN
7389   };
7390
7391   for (i=0; i<4; i++)
7392   {
7393     int test_x, test_y, test_move_dir, test_element;
7394
7395     test_x = good_x + test_xy[i][0];
7396     test_y = good_y + test_xy[i][1];
7397     if (!IN_LEV_FIELD(test_x, test_y))
7398       continue;
7399
7400     test_move_dir =
7401       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7402
7403 #if 0
7404     test_element = Feld[test_x][test_y];
7405 #else
7406     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7407 #endif
7408
7409     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7410        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7411     */
7412     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7413         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
7414     {
7415       kill_x = test_x;
7416       kill_y = test_y;
7417       break;
7418     }
7419   }
7420
7421   if (kill_x != -1 || kill_y != -1)
7422   {
7423     if (IS_PLAYER(good_x, good_y))
7424     {
7425       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7426
7427       if (player->shield_deadly_time_left > 0)
7428         Bang(kill_x, kill_y);
7429       else if (!PLAYER_PROTECTED(good_x, good_y))
7430         KillHero(player);
7431     }
7432     else
7433       Bang(good_x, good_y);
7434   }
7435 }
7436
7437 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7438 {
7439   int i, kill_x = -1, kill_y = -1;
7440   int bad_element = Feld[bad_x][bad_y];
7441   static int test_xy[4][2] =
7442   {
7443     { 0, -1 },
7444     { -1, 0 },
7445     { +1, 0 },
7446     { 0, +1 }
7447   };
7448   static int touch_dir[4] =
7449   {
7450     MV_LEFT | MV_RIGHT,
7451     MV_UP   | MV_DOWN,
7452     MV_UP   | MV_DOWN,
7453     MV_LEFT | MV_RIGHT
7454   };
7455   static int test_dir[4] =
7456   {
7457     MV_UP,
7458     MV_LEFT,
7459     MV_RIGHT,
7460     MV_DOWN
7461   };
7462
7463   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
7464     return;
7465
7466   for (i=0; i<4; i++)
7467   {
7468     int test_x, test_y, test_move_dir, test_element;
7469
7470     test_x = bad_x + test_xy[i][0];
7471     test_y = bad_y + test_xy[i][1];
7472     if (!IN_LEV_FIELD(test_x, test_y))
7473       continue;
7474
7475     test_move_dir =
7476       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7477
7478     test_element = Feld[test_x][test_y];
7479
7480     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7481        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7482     */
7483     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
7484         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
7485     {
7486       /* good thing is player or penguin that does not move away */
7487       if (IS_PLAYER(test_x, test_y))
7488       {
7489         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7490
7491         if (bad_element == EL_ROBOT && player->is_moving)
7492           continue;     /* robot does not kill player if he is moving */
7493
7494         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7495         {
7496           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7497             continue;           /* center and border element do not touch */
7498         }
7499
7500         kill_x = test_x;
7501         kill_y = test_y;
7502         break;
7503       }
7504       else if (test_element == EL_PENGUIN)
7505       {
7506         kill_x = test_x;
7507         kill_y = test_y;
7508         break;
7509       }
7510     }
7511   }
7512
7513   if (kill_x != -1 || kill_y != -1)
7514   {
7515     if (IS_PLAYER(kill_x, kill_y))
7516     {
7517       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7518
7519       if (player->shield_deadly_time_left > 0)
7520         Bang(bad_x, bad_y);
7521       else if (!PLAYER_PROTECTED(kill_x, kill_y))
7522         KillHero(player);
7523     }
7524     else
7525       Bang(kill_x, kill_y);
7526   }
7527 }
7528
7529 void TestIfHeroTouchesBadThing(int x, int y)
7530 {
7531   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7532 }
7533
7534 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7535 {
7536   TestIfGoodThingHitsBadThing(x, y, move_dir);
7537 }
7538
7539 void TestIfBadThingTouchesHero(int x, int y)
7540 {
7541   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7542 }
7543
7544 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7545 {
7546   TestIfBadThingHitsGoodThing(x, y, move_dir);
7547 }
7548
7549 void TestIfFriendTouchesBadThing(int x, int y)
7550 {
7551   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7552 }
7553
7554 void TestIfBadThingTouchesFriend(int x, int y)
7555 {
7556   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7557 }
7558
7559 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7560 {
7561   int i, kill_x = bad_x, kill_y = bad_y;
7562   static int xy[4][2] =
7563   {
7564     { 0, -1 },
7565     { -1, 0 },
7566     { +1, 0 },
7567     { 0, +1 }
7568   };
7569
7570   for (i=0; i<4; i++)
7571   {
7572     int x, y, element;
7573
7574     x = bad_x + xy[i][0];
7575     y = bad_y + xy[i][1];
7576     if (!IN_LEV_FIELD(x, y))
7577       continue;
7578
7579     element = Feld[x][y];
7580     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7581         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7582     {
7583       kill_x = x;
7584       kill_y = y;
7585       break;
7586     }
7587   }
7588
7589   if (kill_x != bad_x || kill_y != bad_y)
7590     Bang(bad_x, bad_y);
7591 }
7592
7593 void KillHero(struct PlayerInfo *player)
7594 {
7595   int jx = player->jx, jy = player->jy;
7596
7597   if (!player->active)
7598     return;
7599
7600   /* remove accessible field at the player's position */
7601   Feld[jx][jy] = EL_EMPTY;
7602
7603   /* deactivate shield (else Bang()/Explode() would not work right) */
7604   player->shield_normal_time_left = 0;
7605   player->shield_deadly_time_left = 0;
7606
7607   Bang(jx, jy);
7608   BuryHero(player);
7609 }
7610
7611 static void KillHeroUnlessProtected(int x, int y)
7612 {
7613   if (!PLAYER_PROTECTED(x, y))
7614     KillHero(PLAYERINFO(x, y));
7615 }
7616
7617 void BuryHero(struct PlayerInfo *player)
7618 {
7619   int jx = player->jx, jy = player->jy;
7620
7621   if (!player->active)
7622     return;
7623
7624 #if 1
7625   PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7626 #else
7627   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7628 #endif
7629   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7630
7631   player->GameOver = TRUE;
7632   RemoveHero(player);
7633 }
7634
7635 void RemoveHero(struct PlayerInfo *player)
7636 {
7637   int jx = player->jx, jy = player->jy;
7638   int i, found = FALSE;
7639
7640   player->present = FALSE;
7641   player->active = FALSE;
7642
7643   if (!ExplodeField[jx][jy])
7644     StorePlayer[jx][jy] = 0;
7645
7646   for (i=0; i<MAX_PLAYERS; i++)
7647     if (stored_player[i].active)
7648       found = TRUE;
7649
7650   if (!found)
7651     AllPlayersGone = TRUE;
7652
7653   ExitX = ZX = jx;
7654   ExitY = ZY = jy;
7655 }
7656
7657 /*
7658   =============================================================================
7659   checkDiagonalPushing()
7660   -----------------------------------------------------------------------------
7661   check if diagonal input device direction results in pushing of object
7662   (by checking if the alternative direction is walkable, diggable, ...)
7663   =============================================================================
7664 */
7665
7666 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7667                                     int x, int y, int real_dx, int real_dy)
7668 {
7669   int jx, jy, dx, dy, xx, yy;
7670
7671   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
7672     return TRUE;
7673
7674   /* diagonal direction: check alternative direction */
7675   jx = player->jx;
7676   jy = player->jy;
7677   dx = x - jx;
7678   dy = y - jy;
7679   xx = jx + (dx == 0 ? real_dx : 0);
7680   yy = jy + (dy == 0 ? real_dy : 0);
7681
7682   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7683 }
7684
7685 /*
7686   =============================================================================
7687   DigField()
7688   -----------------------------------------------------------------------------
7689   x, y:                 field next to player (non-diagonal) to try to dig to
7690   real_dx, real_dy:     direction as read from input device (can be diagonal)
7691   =============================================================================
7692 */
7693
7694 int DigField(struct PlayerInfo *player,
7695              int x, int y, int real_dx, int real_dy, int mode)
7696 {
7697   static int change_sides[4] =
7698   {
7699     CH_SIDE_RIGHT,      /* moving left  */
7700     CH_SIDE_LEFT,       /* moving right */
7701     CH_SIDE_BOTTOM,     /* moving up    */
7702     CH_SIDE_TOP,        /* moving down  */
7703   };
7704   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7705   int jx = player->jx, jy = player->jy;
7706   int dx = x - jx, dy = y - jy;
7707   int nextx = x + dx, nexty = y + dy;
7708   int move_direction = (dx == -1 ? MV_LEFT :
7709                         dx == +1 ? MV_RIGHT :
7710                         dy == -1 ? MV_UP :
7711                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7712   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7713   int element;
7714
7715   if (player->MovPos == 0)
7716   {
7717     player->is_digging = FALSE;
7718     player->is_collecting = FALSE;
7719   }
7720
7721   if (player->MovPos == 0)      /* last pushing move finished */
7722     player->is_pushing = FALSE;
7723
7724   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
7725   {
7726     player->is_switching = FALSE;
7727     player->push_delay = 0;
7728
7729     return MF_NO_ACTION;
7730   }
7731
7732   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7733     return MF_NO_ACTION;
7734
7735 #if 0
7736   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7737 #else
7738   if (IS_TUBE(Feld[jx][jy]) ||
7739       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7740 #endif
7741   {
7742     int i = 0;
7743     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7744     int tube_leave_directions[][2] =
7745     {
7746       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7747       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
7748       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
7749       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
7750       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
7751       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
7752       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
7753       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
7754       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
7755       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
7756       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
7757       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7758     };
7759
7760     while (tube_leave_directions[i][0] != tube_element)
7761     {
7762       i++;
7763       if (tube_leave_directions[i][0] == -1)    /* should not happen */
7764         break;
7765     }
7766
7767     if (!(tube_leave_directions[i][1] & move_direction))
7768       return MF_NO_ACTION;      /* tube has no opening in this direction */
7769   }
7770
7771   element = Feld[x][y];
7772
7773   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7774       game.engine_version >= VERSION_IDENT(2,2,0,0))
7775     return MF_NO_ACTION;
7776
7777   switch (element)
7778   {
7779     case EL_SP_PORT_LEFT:
7780     case EL_SP_PORT_RIGHT:
7781     case EL_SP_PORT_UP:
7782     case EL_SP_PORT_DOWN:
7783     case EL_SP_PORT_HORIZONTAL:
7784     case EL_SP_PORT_VERTICAL:
7785     case EL_SP_PORT_ANY:
7786     case EL_SP_GRAVITY_PORT_LEFT:
7787     case EL_SP_GRAVITY_PORT_RIGHT:
7788     case EL_SP_GRAVITY_PORT_UP:
7789     case EL_SP_GRAVITY_PORT_DOWN:
7790       if ((dx == -1 &&
7791            element != EL_SP_PORT_LEFT &&
7792            element != EL_SP_GRAVITY_PORT_LEFT &&
7793            element != EL_SP_PORT_HORIZONTAL &&
7794            element != EL_SP_PORT_ANY) ||
7795           (dx == +1 &&
7796            element != EL_SP_PORT_RIGHT &&
7797            element != EL_SP_GRAVITY_PORT_RIGHT &&
7798            element != EL_SP_PORT_HORIZONTAL &&
7799            element != EL_SP_PORT_ANY) ||
7800           (dy == -1 &&
7801            element != EL_SP_PORT_UP &&
7802            element != EL_SP_GRAVITY_PORT_UP &&
7803            element != EL_SP_PORT_VERTICAL &&
7804            element != EL_SP_PORT_ANY) ||
7805           (dy == +1 &&
7806            element != EL_SP_PORT_DOWN &&
7807            element != EL_SP_GRAVITY_PORT_DOWN &&
7808            element != EL_SP_PORT_VERTICAL &&
7809            element != EL_SP_PORT_ANY) ||
7810           !IN_LEV_FIELD(nextx, nexty) ||
7811           !IS_FREE(nextx, nexty))
7812         return MF_NO_ACTION;
7813
7814       if (element == EL_SP_GRAVITY_PORT_LEFT ||
7815           element == EL_SP_GRAVITY_PORT_RIGHT ||
7816           element == EL_SP_GRAVITY_PORT_UP ||
7817           element == EL_SP_GRAVITY_PORT_DOWN)
7818         game.gravity = !game.gravity;
7819
7820       /* automatically move to the next field with double speed */
7821       player->programmed_action = move_direction;
7822       DOUBLE_PLAYER_SPEED(player);
7823
7824       PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7825       break;
7826
7827     case EL_TUBE_ANY:
7828     case EL_TUBE_VERTICAL:
7829     case EL_TUBE_HORIZONTAL:
7830     case EL_TUBE_VERTICAL_LEFT:
7831     case EL_TUBE_VERTICAL_RIGHT:
7832     case EL_TUBE_HORIZONTAL_UP:
7833     case EL_TUBE_HORIZONTAL_DOWN:
7834     case EL_TUBE_LEFT_UP:
7835     case EL_TUBE_LEFT_DOWN:
7836     case EL_TUBE_RIGHT_UP:
7837     case EL_TUBE_RIGHT_DOWN:
7838       {
7839         int i = 0;
7840         int tube_enter_directions[][2] =
7841         {
7842           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7843           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
7844           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
7845           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
7846           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
7847           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
7848           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
7849           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
7850           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
7851           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
7852           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
7853           { -1,                         MV_NO_MOVING                         }
7854         };
7855
7856         while (tube_enter_directions[i][0] != element)
7857         {
7858           i++;
7859           if (tube_enter_directions[i][0] == -1)        /* should not happen */
7860             break;
7861         }
7862
7863         if (!(tube_enter_directions[i][1] & move_direction))
7864           return MF_NO_ACTION;  /* tube has no opening in this direction */
7865
7866         PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7867       }
7868       break;
7869
7870     default:
7871
7872       if (IS_WALKABLE(element))
7873       {
7874         int sound_action = ACTION_WALKING;
7875
7876         if (element >= EL_GATE_1 && element <= EL_GATE_4)
7877         {
7878           if (!player->key[element - EL_GATE_1])
7879             return MF_NO_ACTION;
7880         }
7881         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7882         {
7883           if (!player->key[element - EL_GATE_1_GRAY])
7884             return MF_NO_ACTION;
7885         }
7886         else if (element == EL_EXIT_OPEN ||
7887                  element == EL_SP_EXIT_OPEN ||
7888                  element == EL_SP_EXIT_OPENING)
7889         {
7890           sound_action = ACTION_PASSING;        /* player is passing exit */
7891         }
7892         else if (element == EL_EMPTY)
7893         {
7894           sound_action = ACTION_MOVING;         /* nothing to walk on */
7895         }
7896
7897         /* play sound from background or player, whatever is available */
7898         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7899           PlaySoundLevelElementAction(x, y, element, sound_action);
7900         else
7901           PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7902
7903         break;
7904       }
7905       else if (IS_PASSABLE(element))
7906       {
7907         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7908           return MF_NO_ACTION;
7909
7910 #if 1
7911         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
7912           return MF_NO_ACTION;
7913 #endif
7914
7915         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7916         {
7917           if (!player->key[element - EL_EM_GATE_1])
7918             return MF_NO_ACTION;
7919         }
7920         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7921         {
7922           if (!player->key[element - EL_EM_GATE_1_GRAY])
7923             return MF_NO_ACTION;
7924         }
7925
7926         /* automatically move to the next field with double speed */
7927         player->programmed_action = move_direction;
7928         DOUBLE_PLAYER_SPEED(player);
7929
7930         PlaySoundLevelAction(x, y, ACTION_PASSING);
7931
7932         break;
7933       }
7934       else if (IS_DIGGABLE(element))
7935       {
7936         RemoveField(x, y);
7937
7938         if (mode != DF_SNAP)
7939         {
7940 #if 1
7941           GfxElement[x][y] = GFX_ELEMENT(element);
7942 #else
7943           GfxElement[x][y] =
7944             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7945 #endif
7946           player->is_digging = TRUE;
7947         }
7948
7949         PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7950
7951         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7952
7953 #if 1
7954         if (mode == DF_SNAP)
7955           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7956 #endif
7957
7958         break;
7959       }
7960       else if (IS_COLLECTIBLE(element))
7961       {
7962         RemoveField(x, y);
7963
7964         if (mode != DF_SNAP)
7965         {
7966           GfxElement[x][y] = element;
7967           player->is_collecting = TRUE;
7968         }
7969
7970         if (element == EL_SPEED_PILL)
7971           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7972         else if (element == EL_EXTRA_TIME && level.time > 0)
7973         {
7974           TimeLeft += 10;
7975           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7976         }
7977         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7978         {
7979           player->shield_normal_time_left += 10;
7980           if (element == EL_SHIELD_DEADLY)
7981             player->shield_deadly_time_left += 10;
7982         }
7983         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7984         {
7985           if (player->inventory_size < MAX_INVENTORY_SIZE)
7986             player->inventory_element[player->inventory_size++] = element;
7987
7988           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7989                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7990         }
7991         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7992         {
7993           player->dynabomb_count++;
7994           player->dynabombs_left++;
7995         }
7996         else if (element == EL_DYNABOMB_INCREASE_SIZE)
7997         {
7998           player->dynabomb_size++;
7999         }
8000         else if (element == EL_DYNABOMB_INCREASE_POWER)
8001         {
8002           player->dynabomb_xl = TRUE;
8003         }
8004         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
8005                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
8006         {
8007           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
8008                         element - EL_KEY_1 : element - EL_EM_KEY_1);
8009
8010           player->key[key_nr] = TRUE;
8011
8012           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
8013                              el2edimg(EL_KEY_1 + key_nr));
8014           redraw_mask |= REDRAW_DOOR_1;
8015         }
8016         else if (IS_ENVELOPE(element))
8017         {
8018 #if 1
8019           player->show_envelope = element;
8020 #else
8021           ShowEnvelope(element - EL_ENVELOPE_1);
8022 #endif
8023         }
8024         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
8025         {
8026           int i;
8027
8028           for (i=0; i < element_info[element].collect_count; i++)
8029             if (player->inventory_size < MAX_INVENTORY_SIZE)
8030               player->inventory_element[player->inventory_size++] = element;
8031
8032           DrawText(DX_DYNAMITE, DY_DYNAMITE,
8033                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8034         }
8035         else if (element_info[element].collect_count > 0)
8036         {
8037           local_player->gems_still_needed -=
8038             element_info[element].collect_count;
8039           if (local_player->gems_still_needed < 0)
8040             local_player->gems_still_needed = 0;
8041
8042           DrawText(DX_EMERALDS, DY_EMERALDS,
8043                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
8044         }
8045
8046         RaiseScoreElement(element);
8047         PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
8048
8049         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
8050
8051 #if 1
8052         if (mode == DF_SNAP)
8053           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
8054 #endif
8055
8056         break;
8057       }
8058       else if (IS_PUSHABLE(element))
8059       {
8060         if (mode == DF_SNAP && element != EL_BD_ROCK)
8061           return MF_NO_ACTION;
8062
8063         if (CAN_FALL(element) && dy)
8064           return MF_NO_ACTION;
8065
8066         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
8067             !(element == EL_SPRING && use_spring_bug))
8068           return MF_NO_ACTION;
8069
8070 #if 1
8071         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
8072             ((move_direction & MV_VERTICAL &&
8073               ((element_info[element].move_pattern & MV_LEFT &&
8074                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
8075                (element_info[element].move_pattern & MV_RIGHT &&
8076                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
8077              (move_direction & MV_HORIZONTAL &&
8078               ((element_info[element].move_pattern & MV_UP &&
8079                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
8080                (element_info[element].move_pattern & MV_DOWN &&
8081                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
8082           return MF_NO_ACTION;
8083 #endif
8084
8085 #if 1
8086         /* do not push elements already moving away faster than player */
8087         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
8088             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
8089           return MF_NO_ACTION;
8090 #else
8091         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
8092           return MF_NO_ACTION;
8093 #endif
8094
8095 #if 1
8096         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8097         {
8098           if (player->push_delay_value == -1)
8099             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8100         }
8101         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
8102         {
8103           if (!player->is_pushing)
8104             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8105         }
8106
8107         /*
8108         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
8109             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
8110              !player_is_pushing))
8111           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8112         */
8113 #else
8114         if (!player->is_pushing &&
8115             game.engine_version >= VERSION_IDENT(2,2,0,7))
8116           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8117 #endif
8118
8119 #if 0
8120         printf("::: push delay: %ld [%d, %d] [%d]\n",
8121                player->push_delay_value, FrameCounter, game.engine_version,
8122                player->is_pushing);
8123 #endif
8124
8125         player->is_pushing = TRUE;
8126
8127         if (!(IN_LEV_FIELD(nextx, nexty) &&
8128               (IS_FREE(nextx, nexty) ||
8129                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8130                 IS_SB_ELEMENT(element)))))
8131           return MF_NO_ACTION;
8132
8133         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8134           return MF_NO_ACTION;
8135
8136         if (player->push_delay == 0)    /* new pushing; restart delay */
8137           player->push_delay = FrameCounter;
8138
8139         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8140             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8141             element != EL_SPRING && element != EL_BALLOON)
8142         {
8143           /* make sure that there is no move delay before next try to push */
8144           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8145             player->move_delay = INITIAL_MOVE_DELAY_OFF;
8146
8147           return MF_NO_ACTION;
8148         }
8149
8150 #if 0
8151         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8152 #endif
8153
8154         if (IS_SB_ELEMENT(element))
8155         {
8156           if (element == EL_SOKOBAN_FIELD_FULL)
8157           {
8158             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8159             local_player->sokobanfields_still_needed++;
8160           }
8161
8162           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8163           {
8164             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8165             local_player->sokobanfields_still_needed--;
8166           }
8167
8168           Feld[x][y] = EL_SOKOBAN_OBJECT;
8169
8170           if (Back[x][y] == Back[nextx][nexty])
8171             PlaySoundLevelAction(x, y, ACTION_PUSHING);
8172           else if (Back[x][y] != 0)
8173             PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8174                                         ACTION_EMPTYING);
8175           else
8176             PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8177                                         ACTION_FILLING);
8178
8179           if (local_player->sokobanfields_still_needed == 0 &&
8180               game.emulation == EMU_SOKOBAN)
8181           {
8182             player->LevelSolved = player->GameOver = TRUE;
8183             PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8184           }
8185         }
8186         else
8187           PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8188
8189         InitMovingField(x, y, move_direction);
8190         GfxAction[x][y] = ACTION_PUSHING;
8191
8192         if (mode == DF_SNAP)
8193           ContinueMoving(x, y);
8194         else
8195           MovPos[x][y] = (dx != 0 ? dx : dy);
8196
8197         Pushed[x][y] = TRUE;
8198         Pushed[nextx][nexty] = TRUE;
8199
8200         if (game.engine_version < VERSION_IDENT(2,2,0,7))
8201           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8202         else
8203           player->push_delay_value = -1;        /* get new value later */
8204
8205         CheckTriggeredElementSideChange(x, y, element, dig_side,
8206                                         CE_OTHER_GETS_PUSHED);
8207         CheckElementSideChange(x, y, element, dig_side,
8208                                CE_PUSHED_BY_PLAYER, -1);
8209
8210         break;
8211       }
8212       else if (IS_SWITCHABLE(element))
8213       {
8214         if (PLAYER_SWITCHING(player, x, y))
8215           return MF_ACTION;
8216
8217         player->is_switching = TRUE;
8218         player->switch_x = x;
8219         player->switch_y = y;
8220
8221         PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8222
8223         if (element == EL_ROBOT_WHEEL)
8224         {
8225           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8226           ZX = x;
8227           ZY = y;
8228
8229           DrawLevelField(x, y);
8230         }
8231         else if (element == EL_SP_TERMINAL)
8232         {
8233           int xx, yy;
8234
8235           for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8236           {
8237             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8238               Bang(xx, yy);
8239             else if (Feld[xx][yy] == EL_SP_TERMINAL)
8240               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8241           }
8242         }
8243         else if (IS_BELT_SWITCH(element))
8244         {
8245           ToggleBeltSwitch(x, y);
8246         }
8247         else if (element == EL_SWITCHGATE_SWITCH_UP ||
8248                  element == EL_SWITCHGATE_SWITCH_DOWN)
8249         {
8250           ToggleSwitchgateSwitch(x, y);
8251         }
8252         else if (element == EL_LIGHT_SWITCH ||
8253                  element == EL_LIGHT_SWITCH_ACTIVE)
8254         {
8255           ToggleLightSwitch(x, y);
8256
8257 #if 0
8258           PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8259                          SND_LIGHT_SWITCH_ACTIVATING :
8260                          SND_LIGHT_SWITCH_DEACTIVATING);
8261 #endif
8262         }
8263         else if (element == EL_TIMEGATE_SWITCH)
8264         {
8265           ActivateTimegateSwitch(x, y);
8266         }
8267         else if (element == EL_BALLOON_SWITCH_LEFT ||
8268                  element == EL_BALLOON_SWITCH_RIGHT ||
8269                  element == EL_BALLOON_SWITCH_UP ||
8270                  element == EL_BALLOON_SWITCH_DOWN ||
8271                  element == EL_BALLOON_SWITCH_ANY)
8272         {
8273           if (element == EL_BALLOON_SWITCH_ANY)
8274             game.balloon_dir = move_direction;
8275           else
8276             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
8277                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8278                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
8279                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
8280                                 MV_NO_MOVING);
8281         }
8282         else if (element == EL_LAMP)
8283         {
8284           Feld[x][y] = EL_LAMP_ACTIVE;
8285           local_player->lights_still_needed--;
8286
8287           DrawLevelField(x, y);
8288         }
8289         else if (element == EL_TIME_ORB_FULL)
8290         {
8291           Feld[x][y] = EL_TIME_ORB_EMPTY;
8292           TimeLeft += 10;
8293           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8294
8295           DrawLevelField(x, y);
8296
8297 #if 0
8298           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8299 #endif
8300         }
8301
8302         return MF_ACTION;
8303       }
8304       else
8305       {
8306         if (!PLAYER_SWITCHING(player, x, y))
8307         {
8308           player->is_switching = TRUE;
8309           player->switch_x = x;
8310           player->switch_y = y;
8311
8312           CheckTriggeredElementSideChange(x, y, element, dig_side,
8313                                           CE_OTHER_IS_SWITCHING);
8314           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8315         }
8316
8317         CheckTriggeredElementSideChange(x, y, element, dig_side,
8318                                         CE_OTHER_GETS_PRESSED);
8319         CheckElementSideChange(x, y, element, dig_side,
8320                                CE_PRESSED_BY_PLAYER, -1);
8321       }
8322
8323       return MF_NO_ACTION;
8324   }
8325
8326   player->push_delay = 0;
8327
8328   if (Feld[x][y] != element)            /* really digged/collected something */
8329     player->is_collecting = !player->is_digging;
8330
8331   return MF_MOVING;
8332 }
8333
8334 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8335 {
8336   int jx = player->jx, jy = player->jy;
8337   int x = jx + dx, y = jy + dy;
8338   int snap_direction = (dx == -1 ? MV_LEFT :
8339                         dx == +1 ? MV_RIGHT :
8340                         dy == -1 ? MV_UP :
8341                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8342
8343   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8344     return FALSE;
8345
8346   if (!player->active || !IN_LEV_FIELD(x, y))
8347     return FALSE;
8348
8349   if (dx && dy)
8350     return FALSE;
8351
8352   if (!dx && !dy)
8353   {
8354     if (player->MovPos == 0)
8355       player->is_pushing = FALSE;
8356
8357     player->is_snapping = FALSE;
8358
8359     if (player->MovPos == 0)
8360     {
8361       player->is_moving = FALSE;
8362       player->is_digging = FALSE;
8363       player->is_collecting = FALSE;
8364     }
8365
8366     return FALSE;
8367   }
8368
8369   if (player->is_snapping)
8370     return FALSE;
8371
8372   player->MovDir = snap_direction;
8373
8374   player->is_moving = FALSE;
8375   player->is_digging = FALSE;
8376   player->is_collecting = FALSE;
8377
8378   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8379     return FALSE;
8380
8381   player->is_snapping = TRUE;
8382
8383   player->is_moving = FALSE;
8384   player->is_digging = FALSE;
8385   player->is_collecting = FALSE;
8386
8387   DrawLevelField(x, y);
8388   BackToFront();
8389
8390   return TRUE;
8391 }
8392
8393 boolean DropElement(struct PlayerInfo *player)
8394 {
8395   int jx = player->jx, jy = player->jy;
8396   int old_element;
8397
8398   if (!player->active || player->MovPos)
8399     return FALSE;
8400
8401   old_element = Feld[jx][jy];
8402
8403   /* check if player has anything that can be dropped */
8404   if (player->inventory_size == 0 && player->dynabombs_left == 0)
8405     return FALSE;
8406
8407   /* check if anything can be dropped at the current position */
8408   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8409     return FALSE;
8410
8411   /* collected custom elements can only be dropped on empty fields */
8412   if (player->inventory_size > 0 &&
8413       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8414       && old_element != EL_EMPTY)
8415     return FALSE;
8416
8417   if (old_element != EL_EMPTY)
8418     Back[jx][jy] = old_element;         /* store old element on this field */
8419
8420   MovDelay[jx][jy] = 96;
8421
8422   ResetGfxAnimation(jx, jy);
8423   ResetRandomAnimationValue(jx, jy);
8424
8425   if (player->inventory_size > 0)
8426   {
8427     int new_element = player->inventory_element[--player->inventory_size];
8428
8429     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8430                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8431                     new_element);
8432
8433     DrawText(DX_DYNAMITE, DY_DYNAMITE,
8434              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8435
8436     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8437       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8438
8439     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8440
8441     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8442     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8443
8444     TestIfElementTouchesCustomElement(jx, jy);
8445   }
8446   else          /* player is dropping a dyna bomb */
8447   {
8448     player->dynabombs_left--;
8449
8450     Feld[jx][jy] =
8451       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8452
8453     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8454       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8455
8456     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8457   }
8458
8459   return TRUE;
8460 }
8461
8462 /* ------------------------------------------------------------------------- */
8463 /* game sound playing functions                                              */
8464 /* ------------------------------------------------------------------------- */
8465
8466 static int *loop_sound_frame = NULL;
8467 static int *loop_sound_volume = NULL;
8468
8469 void InitPlaySoundLevel()
8470 {
8471   int num_sounds = getSoundListSize();
8472
8473   if (loop_sound_frame != NULL)
8474     free(loop_sound_frame);
8475
8476   if (loop_sound_volume != NULL)
8477     free(loop_sound_volume);
8478
8479   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8480   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8481 }
8482
8483 static void PlaySoundLevel(int x, int y, int nr)
8484 {
8485   int sx = SCREENX(x), sy = SCREENY(y);
8486   int volume, stereo_position;
8487   int max_distance = 8;
8488   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8489
8490   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8491       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8492     return;
8493
8494   if (!IN_LEV_FIELD(x, y) ||
8495       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8496       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8497     return;
8498
8499   volume = SOUND_MAX_VOLUME;
8500
8501   if (!IN_SCR_FIELD(sx, sy))
8502   {
8503     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8504     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8505
8506     volume -= volume * (dx > dy ? dx : dy) / max_distance;
8507   }
8508
8509   stereo_position = (SOUND_MAX_LEFT +
8510                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8511                      (SCR_FIELDX + 2 * max_distance));
8512
8513   if (IS_LOOP_SOUND(nr))
8514   {
8515     /* This assures that quieter loop sounds do not overwrite louder ones,
8516        while restarting sound volume comparison with each new game frame. */
8517
8518     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8519       return;
8520
8521     loop_sound_volume[nr] = volume;
8522     loop_sound_frame[nr] = FrameCounter;
8523   }
8524
8525   PlaySoundExt(nr, volume, stereo_position, type);
8526 }
8527
8528 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8529 {
8530   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8531                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
8532                  y < LEVELY(BY1) ? LEVELY(BY1) :
8533                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
8534                  sound_action);
8535 }
8536
8537 static void PlaySoundLevelAction(int x, int y, int action)
8538 {
8539   PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8540 }
8541
8542 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8543 {
8544   int sound_effect = element_info[element].sound[action];
8545
8546   if (sound_effect != SND_UNDEFINED)
8547     PlaySoundLevel(x, y, sound_effect);
8548 }
8549
8550 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8551 {
8552   int sound_effect = element_info[Feld[x][y]].sound[action];
8553
8554   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8555     PlaySoundLevel(x, y, sound_effect);
8556 }
8557
8558 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8559 {
8560   int sound_effect = element_info[Feld[x][y]].sound[action];
8561
8562   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8563     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8564 }
8565
8566 void RaiseScore(int value)
8567 {
8568   local_player->score += value;
8569   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8570 }
8571
8572 void RaiseScoreElement(int element)
8573 {
8574   switch(element)
8575   {
8576     case EL_EMERALD:
8577     case EL_BD_DIAMOND:
8578     case EL_EMERALD_YELLOW:
8579     case EL_EMERALD_RED:
8580     case EL_EMERALD_PURPLE:
8581     case EL_SP_INFOTRON:
8582       RaiseScore(level.score[SC_EMERALD]);
8583       break;
8584     case EL_DIAMOND:
8585       RaiseScore(level.score[SC_DIAMOND]);
8586       break;
8587     case EL_CRYSTAL:
8588       RaiseScore(level.score[SC_CRYSTAL]);
8589       break;
8590     case EL_PEARL:
8591       RaiseScore(level.score[SC_PEARL]);
8592       break;
8593     case EL_BUG:
8594     case EL_BD_BUTTERFLY:
8595     case EL_SP_ELECTRON:
8596       RaiseScore(level.score[SC_BUG]);
8597       break;
8598     case EL_SPACESHIP:
8599     case EL_BD_FIREFLY:
8600     case EL_SP_SNIKSNAK:
8601       RaiseScore(level.score[SC_SPACESHIP]);
8602       break;
8603     case EL_YAMYAM:
8604     case EL_DARK_YAMYAM:
8605       RaiseScore(level.score[SC_YAMYAM]);
8606       break;
8607     case EL_ROBOT:
8608       RaiseScore(level.score[SC_ROBOT]);
8609       break;
8610     case EL_PACMAN:
8611       RaiseScore(level.score[SC_PACMAN]);
8612       break;
8613     case EL_NUT:
8614       RaiseScore(level.score[SC_NUT]);
8615       break;
8616     case EL_DYNAMITE:
8617     case EL_SP_DISK_RED:
8618     case EL_DYNABOMB_INCREASE_NUMBER:
8619     case EL_DYNABOMB_INCREASE_SIZE:
8620     case EL_DYNABOMB_INCREASE_POWER:
8621       RaiseScore(level.score[SC_DYNAMITE]);
8622       break;
8623     case EL_SHIELD_NORMAL:
8624     case EL_SHIELD_DEADLY:
8625       RaiseScore(level.score[SC_SHIELD]);
8626       break;
8627     case EL_EXTRA_TIME:
8628       RaiseScore(level.score[SC_TIME_BONUS]);
8629       break;
8630     case EL_KEY_1:
8631     case EL_KEY_2:
8632     case EL_KEY_3:
8633     case EL_KEY_4:
8634       RaiseScore(level.score[SC_KEY]);
8635       break;
8636     default:
8637       RaiseScore(element_info[element].collect_score);
8638       break;
8639   }
8640 }
8641
8642 void RequestQuitGame(boolean ask_if_really_quit)
8643 {
8644   if (AllPlayersGone ||
8645       !ask_if_really_quit ||
8646       level_editor_test_game ||
8647       Request("Do you really want to quit the game ?",
8648               REQ_ASK | REQ_STAY_CLOSED))
8649   {
8650 #if defined(PLATFORM_UNIX)
8651     if (options.network)
8652       SendToServer_StopPlaying();
8653     else
8654 #endif
8655     {
8656       game_status = GAME_MODE_MAIN;
8657       DrawMainMenu();
8658     }
8659   }
8660   else
8661   {
8662     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8663   }
8664 }
8665
8666
8667 /* ---------- new game button stuff ---------------------------------------- */
8668
8669 /* graphic position values for game buttons */
8670 #define GAME_BUTTON_XSIZE       30
8671 #define GAME_BUTTON_YSIZE       30
8672 #define GAME_BUTTON_XPOS        5
8673 #define GAME_BUTTON_YPOS        215
8674 #define SOUND_BUTTON_XPOS       5
8675 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8676
8677 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8678 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8679 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8680 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8681 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8682 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8683
8684 static struct
8685 {
8686   int x, y;
8687   int gadget_id;
8688   char *infotext;
8689 } gamebutton_info[NUM_GAME_BUTTONS] =
8690 {
8691   {
8692     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
8693     GAME_CTRL_ID_STOP,
8694     "stop game"
8695   },
8696   {
8697     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
8698     GAME_CTRL_ID_PAUSE,
8699     "pause game"
8700   },
8701   {
8702     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
8703     GAME_CTRL_ID_PLAY,
8704     "play game"
8705   },
8706   {
8707     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
8708     SOUND_CTRL_ID_MUSIC,
8709     "background music on/off"
8710   },
8711   {
8712     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
8713     SOUND_CTRL_ID_LOOPS,
8714     "sound loops on/off"
8715   },
8716   {
8717     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
8718     SOUND_CTRL_ID_SIMPLE,
8719     "normal sounds on/off"
8720   }
8721 };
8722
8723 void CreateGameButtons()
8724 {
8725   int i;
8726
8727   for (i=0; i<NUM_GAME_BUTTONS; i++)
8728   {
8729     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8730     struct GadgetInfo *gi;
8731     int button_type;
8732     boolean checked;
8733     unsigned long event_mask;
8734     int gd_xoffset, gd_yoffset;
8735     int gd_x1, gd_x2, gd_y1, gd_y2;
8736     int id = i;
8737
8738     gd_xoffset = gamebutton_info[i].x;
8739     gd_yoffset = gamebutton_info[i].y;
8740     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8741     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8742
8743     if (id == GAME_CTRL_ID_STOP ||
8744         id == GAME_CTRL_ID_PAUSE ||
8745         id == GAME_CTRL_ID_PLAY)
8746     {
8747       button_type = GD_TYPE_NORMAL_BUTTON;
8748       checked = FALSE;
8749       event_mask = GD_EVENT_RELEASED;
8750       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8751       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8752     }
8753     else
8754     {
8755       button_type = GD_TYPE_CHECK_BUTTON;
8756       checked =
8757         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8758          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8759          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8760       event_mask = GD_EVENT_PRESSED;
8761       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
8762       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8763     }
8764
8765     gi = CreateGadget(GDI_CUSTOM_ID, id,
8766                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
8767                       GDI_X, DX + gd_xoffset,
8768                       GDI_Y, DY + gd_yoffset,
8769                       GDI_WIDTH, GAME_BUTTON_XSIZE,
8770                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
8771                       GDI_TYPE, button_type,
8772                       GDI_STATE, GD_BUTTON_UNPRESSED,
8773                       GDI_CHECKED, checked,
8774                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8775                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8776                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8777                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8778                       GDI_EVENT_MASK, event_mask,
8779                       GDI_CALLBACK_ACTION, HandleGameButtons,
8780                       GDI_END);
8781
8782     if (gi == NULL)
8783       Error(ERR_EXIT, "cannot create gadget");
8784
8785     game_gadget[id] = gi;
8786   }
8787 }
8788
8789 void FreeGameButtons()
8790 {
8791   int i;
8792
8793   for (i=0; i<NUM_GAME_BUTTONS; i++)
8794     FreeGadget(game_gadget[i]);
8795 }
8796
8797 static void MapGameButtons()
8798 {
8799   int i;
8800
8801   for (i=0; i<NUM_GAME_BUTTONS; i++)
8802     MapGadget(game_gadget[i]);
8803 }
8804
8805 void UnmapGameButtons()
8806 {
8807   int i;
8808
8809   for (i=0; i<NUM_GAME_BUTTONS; i++)
8810     UnmapGadget(game_gadget[i]);
8811 }
8812
8813 static void HandleGameButtons(struct GadgetInfo *gi)
8814 {
8815   int id = gi->custom_id;
8816
8817   if (game_status != GAME_MODE_PLAYING)
8818     return;
8819
8820   switch (id)
8821   {
8822     case GAME_CTRL_ID_STOP:
8823       RequestQuitGame(TRUE);
8824       break;
8825
8826     case GAME_CTRL_ID_PAUSE:
8827       if (options.network)
8828       {
8829 #if defined(PLATFORM_UNIX)
8830         if (tape.pausing)
8831           SendToServer_ContinuePlaying();
8832         else
8833           SendToServer_PausePlaying();
8834 #endif
8835       }
8836       else
8837         TapeTogglePause(TAPE_TOGGLE_MANUAL);
8838       break;
8839
8840     case GAME_CTRL_ID_PLAY:
8841       if (tape.pausing)
8842       {
8843 #if defined(PLATFORM_UNIX)
8844         if (options.network)
8845           SendToServer_ContinuePlaying();
8846         else
8847 #endif
8848         {
8849           tape.pausing = FALSE;
8850           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8851         }
8852       }
8853       break;
8854
8855     case SOUND_CTRL_ID_MUSIC:
8856       if (setup.sound_music)
8857       { 
8858         setup.sound_music = FALSE;
8859         FadeMusic();
8860       }
8861       else if (audio.music_available)
8862       { 
8863         setup.sound = setup.sound_music = TRUE;
8864
8865         SetAudioMode(setup.sound);
8866         PlayMusic(level_nr);
8867       }
8868       break;
8869
8870     case SOUND_CTRL_ID_LOOPS:
8871       if (setup.sound_loops)
8872         setup.sound_loops = FALSE;
8873       else if (audio.loops_available)
8874       {
8875         setup.sound = setup.sound_loops = TRUE;
8876         SetAudioMode(setup.sound);
8877       }
8878       break;
8879
8880     case SOUND_CTRL_ID_SIMPLE:
8881       if (setup.sound_simple)
8882         setup.sound_simple = FALSE;
8883       else if (audio.sound_available)
8884       {
8885         setup.sound = setup.sound_simple = TRUE;
8886         SetAudioMode(setup.sound);
8887       }
8888       break;
8889
8890     default:
8891       break;
8892   }
8893 }