rnd-20031104-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME                 29
63 #define YY_TIME                 194
64
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL                (DX + XX_LEVEL)
67 #define DY_LEVEL                (DY + YY_LEVEL)
68 #define DX_EMERALDS             (DX + XX_EMERALDS)
69 #define DY_EMERALDS             (DY + YY_EMERALDS)
70 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
72 #define DX_KEYS                 (DX + XX_KEYS)
73 #define DY_KEYS                 (DY + YY_KEYS)
74 #define DX_SCORE                (DX + XX_SCORE)
75 #define DY_SCORE                (DY + YY_SCORE)
76 #define DX_TIME                 (DX + XX_TIME)
77 #define DY_TIME                 (DY + YY_TIME)
78
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF  -1
81 #define INITIAL_MOVE_DELAY_ON   0
82
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED   4
86
87 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
91
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
94
95 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
96
97 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
98                                  RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
100                                  RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                     (element_info[e].move_delay_random))
103
104 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
105                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
106                                         (condition) ||                  \
107                                         (DONT_COLLIDE_WITH(e) &&        \
108                                          IS_FREE_OR_PLAYER(x, y))))
109
110 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
111                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
112                                         (condition)))
113
114 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
115         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
116
117 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
118         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
119
120 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
121         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
122
123 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
124
125 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
126                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
127                                         Feld[x][y] == EL_DIAMOND))
128
129 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
130                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
131                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
132
133 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
134                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
135                                         IS_AMOEBOID(Feld[x][y])))
136
137 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
138                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
139                                         IS_FOOD_PIG(Feld[x][y])))
140
141 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
142                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
143                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
144                                         Feld[x][y] == EL_EXIT_OPEN ||   \
145                                         Feld[x][y] == EL_ACID))
146
147 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
148                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
149
150 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
151 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
152
153 /* game button identifiers */
154 #define GAME_CTRL_ID_STOP               0
155 #define GAME_CTRL_ID_PAUSE              1
156 #define GAME_CTRL_ID_PLAY               2
157 #define SOUND_CTRL_ID_MUSIC             3
158 #define SOUND_CTRL_ID_LOOPS             4
159 #define SOUND_CTRL_ID_SIMPLE            5
160
161 #define NUM_GAME_BUTTONS                6
162
163
164 /* forward declaration for internal use */
165
166 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
167 static boolean MovePlayer(struct PlayerInfo *, int, int);
168 static void ScrollPlayer(struct PlayerInfo *, int);
169 static void ScrollScreen(struct PlayerInfo *, int);
170
171 static void InitBeltMovement(void);
172 static void CloseAllOpenTimegates(void);
173 static void CheckGravityMovement(struct PlayerInfo *);
174 static void KillHeroUnlessProtected(int, int);
175
176 static void TestIfPlayerTouchesCustomElement(int, int);
177 static void TestIfElementTouchesCustomElement(int, int);
178
179 static void ChangeElement(int, int, int);
180 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
181 static boolean CheckTriggeredElementChange(int, int, int, int);
182 static boolean CheckElementSideChange(int, int, int, int, int, int);
183 static boolean CheckElementChange(int, int, int, int);
184
185 static void 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 1
3737   GfxDir[x][y] = MovDir[x][y];
3738 #endif
3739
3740   TurnRoundExt(x, y);
3741
3742 #if 0
3743   GfxDir[x][y] = MovDir[x][y];
3744 #endif
3745
3746   if (direction != MovDir[x][y])
3747     GfxFrame[x][y] = 0;
3748
3749 #if 0
3750   if (MovDelay[x][y])
3751     GfxAction[x][y] = ACTION_XXX_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   /* !!! this should be handled more generic (not only for mole) !!! */
3790   if (MovDelay[x][y] == 0)
3791     GfxAction[x][y] = ACTION_DEFAULT;
3792 #else
3793   /* !!! this should be handled more generic (not only for mole) !!! */
3794   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
3795     GfxAction[x][y] = ACTION_DEFAULT;
3796 #endif
3797
3798   if (CAN_FALL(element) && y < lev_fieldy - 1)
3799   {
3800     if ((x > 0 && IS_PLAYER(x - 1, y)) ||
3801         (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
3802       if (JustBeingPushed(x, y))
3803         return;
3804
3805     if (element == EL_QUICKSAND_FULL)
3806     {
3807       if (IS_FREE(x, y + 1))
3808       {
3809         InitMovingField(x, y, MV_DOWN);
3810         started_moving = TRUE;
3811
3812         Feld[x][y] = EL_QUICKSAND_EMPTYING;
3813         Store[x][y] = EL_ROCK;
3814 #if 1
3815         PlaySoundLevelAction(x, y, ACTION_EMPTYING);
3816 #else
3817         PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
3818 #endif
3819       }
3820       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3821       {
3822         if (!MovDelay[x][y])
3823           MovDelay[x][y] = TILEY + 1;
3824
3825         if (MovDelay[x][y])
3826         {
3827           MovDelay[x][y]--;
3828           if (MovDelay[x][y])
3829             return;
3830         }
3831
3832         Feld[x][y] = EL_QUICKSAND_EMPTY;
3833         Feld[x][y + 1] = EL_QUICKSAND_FULL;
3834         Store[x][y + 1] = Store[x][y];
3835         Store[x][y] = 0;
3836 #if 1
3837         PlaySoundLevelAction(x, y, ACTION_FILLING);
3838 #else
3839         PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3840 #endif
3841       }
3842     }
3843     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
3844              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
3845     {
3846       InitMovingField(x, y, MV_DOWN);
3847       started_moving = TRUE;
3848
3849       Feld[x][y] = EL_QUICKSAND_FILLING;
3850       Store[x][y] = element;
3851 #if 1
3852       PlaySoundLevelAction(x, y, ACTION_FILLING);
3853 #else
3854       PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
3855 #endif
3856     }
3857     else if (element == EL_MAGIC_WALL_FULL)
3858     {
3859       if (IS_FREE(x, y + 1))
3860       {
3861         InitMovingField(x, y, MV_DOWN);
3862         started_moving = TRUE;
3863
3864         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
3865         Store[x][y] = EL_CHANGED(Store[x][y]);
3866       }
3867       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3868       {
3869         if (!MovDelay[x][y])
3870           MovDelay[x][y] = TILEY/4 + 1;
3871
3872         if (MovDelay[x][y])
3873         {
3874           MovDelay[x][y]--;
3875           if (MovDelay[x][y])
3876             return;
3877         }
3878
3879         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
3880         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
3881         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
3882         Store[x][y] = 0;
3883       }
3884     }
3885     else if (element == EL_BD_MAGIC_WALL_FULL)
3886     {
3887       if (IS_FREE(x, y + 1))
3888       {
3889         InitMovingField(x, y, MV_DOWN);
3890         started_moving = TRUE;
3891
3892         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
3893         Store[x][y] = EL_CHANGED2(Store[x][y]);
3894       }
3895       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3896       {
3897         if (!MovDelay[x][y])
3898           MovDelay[x][y] = TILEY/4 + 1;
3899
3900         if (MovDelay[x][y])
3901         {
3902           MovDelay[x][y]--;
3903           if (MovDelay[x][y])
3904             return;
3905         }
3906
3907         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
3908         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
3909         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
3910         Store[x][y] = 0;
3911       }
3912     }
3913     else if (CAN_PASS_MAGIC_WALL(element) &&
3914              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3915               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3916     {
3917       InitMovingField(x, y, MV_DOWN);
3918       started_moving = TRUE;
3919
3920       Feld[x][y] =
3921         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
3922          EL_BD_MAGIC_WALL_FILLING);
3923       Store[x][y] = element;
3924     }
3925 #if 0
3926     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
3927 #else
3928     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
3929 #endif
3930     {
3931       SplashAcid(x, y);
3932
3933       InitMovingField(x, y, MV_DOWN);
3934       started_moving = TRUE;
3935
3936       Store[x][y] = EL_ACID;
3937 #if 0
3938       /* !!! TEST !!! better use "_FALLING" etc. !!! */
3939       GfxAction[x][y + 1] = ACTION_ACTIVE;
3940 #endif
3941     }
3942 #if 1
3943     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
3944               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
3945               (Feld[x][y + 1] == EL_BLOCKED)) ||
3946              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
3947               CAN_SMASH(element) && WasJustFalling[x][y] &&
3948               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
3949
3950 #else
3951 #if 1
3952     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
3953              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3954              WasJustMoving[x][y] && !Pushed[x][y + 1])
3955 #else
3956     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
3957              WasJustMoving[x][y])
3958 #endif
3959 #endif
3960
3961     {
3962       /* this is needed for a special case not covered by calling "Impact()"
3963          from "ContinueMoving()": if an element moves to a tile directly below
3964          another element which was just falling on that tile (which was empty
3965          in the previous frame), the falling element above would just stop
3966          instead of smashing the element below (in previous version, the above
3967          element was just checked for "moving" instead of "falling", resulting
3968          in incorrect smashes caused by horizontal movement of the above
3969          element; also, the case of the player being the element to smash was
3970          simply not covered here... :-/ ) */
3971
3972       Impact(x, y);
3973     }
3974     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
3975     {
3976       if (MovDir[x][y] == MV_NO_MOVING)
3977       {
3978         InitMovingField(x, y, MV_DOWN);
3979         started_moving = TRUE;
3980       }
3981     }
3982     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
3983     {
3984       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
3985         MovDir[x][y] = MV_DOWN;
3986
3987       InitMovingField(x, y, MV_DOWN);
3988       started_moving = TRUE;
3989     }
3990     else if (element == EL_AMOEBA_DROP)
3991     {
3992       Feld[x][y] = EL_AMOEBA_GROWING;
3993       Store[x][y] = EL_AMOEBA_WET;
3994     }
3995     /* Store[x][y + 1] must be zero, because:
3996        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
3997     */
3998 #if 0
3999 #if OLD_GAME_BEHAVIOUR
4000     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4001 #else
4002     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4003              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4004              element != EL_DX_SUPABOMB)
4005 #endif
4006 #else
4007     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4008               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4009              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4010              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4011 #endif
4012     {
4013       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4014                                 (IS_FREE(x - 1, y + 1) ||
4015                                  Feld[x - 1][y + 1] == EL_ACID));
4016       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4017                                 (IS_FREE(x + 1, y + 1) ||
4018                                  Feld[x + 1][y + 1] == EL_ACID));
4019       boolean can_fall_any  = (can_fall_left || can_fall_right);
4020       boolean can_fall_both = (can_fall_left && can_fall_right);
4021
4022       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4023       {
4024         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4025
4026         if (slippery_type == SLIPPERY_ONLY_LEFT)
4027           can_fall_right = FALSE;
4028         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4029           can_fall_left = FALSE;
4030         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4031           can_fall_right = FALSE;
4032         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4033           can_fall_left = FALSE;
4034
4035         can_fall_any  = (can_fall_left || can_fall_right);
4036         can_fall_both = (can_fall_left && can_fall_right);
4037       }
4038
4039       if (can_fall_any)
4040       {
4041         if (can_fall_both &&
4042             (game.emulation != EMU_BOULDERDASH &&
4043              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4044           can_fall_left = !(can_fall_right = RND(2));
4045
4046         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4047         started_moving = TRUE;
4048       }
4049     }
4050     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4051     {
4052       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4053       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4054       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4055       int belt_dir = game.belt_dir[belt_nr];
4056
4057       if ((belt_dir == MV_LEFT  && left_is_free) ||
4058           (belt_dir == MV_RIGHT && right_is_free))
4059       {
4060         InitMovingField(x, y, belt_dir);
4061         started_moving = TRUE;
4062
4063         GfxAction[x][y] = ACTION_DEFAULT;
4064       }
4065     }
4066   }
4067
4068   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4069   if (CAN_MOVE(element) && !started_moving)
4070   {
4071     int newx, newy;
4072
4073 #if 1
4074     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4075       return;
4076 #else
4077     if ((element == EL_SATELLITE ||
4078          element == EL_BALLOON ||
4079          element == EL_SPRING)
4080         && JustBeingPushed(x, y))
4081       return;
4082 #endif
4083
4084 #if 0
4085 #if 0
4086     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4087       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4088 #else
4089     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4090     {
4091       Moving2Blocked(x, y, &newx, &newy);
4092       if (Feld[newx][newy] == EL_BLOCKED)
4093         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4094     }
4095 #endif
4096 #endif
4097
4098     if (!MovDelay[x][y])        /* start new movement phase */
4099     {
4100       /* all objects that can change their move direction after each step
4101          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4102
4103       if (element != EL_YAMYAM &&
4104           element != EL_DARK_YAMYAM &&
4105           element != EL_PACMAN &&
4106           !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
4107           element_info[element].move_pattern != MV_TURNING_LEFT &&
4108           element_info[element].move_pattern != MV_TURNING_RIGHT)
4109       {
4110         TurnRound(x, y);
4111
4112         if (MovDelay[x][y] && (element == EL_BUG ||
4113                                element == EL_SPACESHIP ||
4114                                element == EL_SP_SNIKSNAK ||
4115                                element == EL_SP_ELECTRON ||
4116                                element == EL_MOLE))
4117           DrawLevelField(x, y);
4118       }
4119     }
4120
4121     if (MovDelay[x][y])         /* wait some time before next movement */
4122     {
4123       MovDelay[x][y]--;
4124
4125 #if 0
4126       if (element == EL_YAMYAM)
4127       {
4128         printf("::: %d\n",
4129                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4130         DrawLevelElementAnimation(x, y, element);
4131       }
4132 #endif
4133
4134       if (MovDelay[x][y])       /* element still has to wait some time */
4135       {
4136 #if 0
4137         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4138         ResetGfxAnimation(x, y);
4139 #endif
4140
4141 #if 0
4142         if (GfxAction[x][y] != ACTION_WAITING)
4143           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4144
4145         GfxAction[x][y] = ACTION_WAITING;
4146 #endif
4147       }
4148
4149       if (element == EL_ROBOT ||
4150 #if 0
4151           element == EL_PACMAN ||
4152 #endif
4153           element == EL_YAMYAM ||
4154           element == EL_DARK_YAMYAM)
4155       {
4156 #if 0
4157         DrawLevelElementAnimation(x, y, element);
4158 #else
4159         DrawLevelElementAnimationIfNeeded(x, y, element);
4160 #endif
4161         PlaySoundLevelAction(x, y, ACTION_WAITING);
4162       }
4163       else if (element == EL_SP_ELECTRON)
4164         DrawLevelElementAnimationIfNeeded(x, y, element);
4165       else if (element == EL_DRAGON)
4166       {
4167         int i;
4168         int dir = MovDir[x][y];
4169         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4170         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4171         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4172                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4173                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4174                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4175         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4176
4177 #if 0
4178         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4179 #endif
4180
4181         GfxAction[x][y] = ACTION_ATTACKING;
4182
4183         if (IS_PLAYER(x, y))
4184           DrawPlayerField(x, y);
4185         else
4186           DrawLevelField(x, y);
4187
4188         PlaySoundLevelActionIfLoop(x, y, ACTION_ATTACKING);
4189
4190         for (i=1; i <= 3; i++)
4191         {
4192           int xx = x + i * dx;
4193           int yy = y + i * dy;
4194           int sx = SCREENX(xx);
4195           int sy = SCREENY(yy);
4196           int flame_graphic = graphic + (i - 1);
4197
4198           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4199             break;
4200
4201           if (MovDelay[x][y])
4202           {
4203             int flamed = MovingOrBlocked2Element(xx, yy);
4204
4205             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4206               Bang(xx, yy);
4207             else
4208               RemoveMovingField(xx, yy);
4209
4210             Feld[xx][yy] = EL_FLAMES;
4211             if (IN_SCR_FIELD(sx, sy))
4212             {
4213               DrawLevelFieldCrumbledSand(xx, yy);
4214               DrawGraphic(sx, sy, flame_graphic, frame);
4215             }
4216           }
4217           else
4218           {
4219             if (Feld[xx][yy] == EL_FLAMES)
4220               Feld[xx][yy] = EL_EMPTY;
4221             DrawLevelField(xx, yy);
4222           }
4223         }
4224       }
4225
4226       if (MovDelay[x][y])       /* element still has to wait some time */
4227       {
4228         PlaySoundLevelAction(x, y, ACTION_WAITING);
4229
4230         return;
4231       }
4232
4233 #if 0
4234       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4235          for all other elements GfxAction will be set by InitMovingField() */
4236       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4237         GfxAction[x][y] = ACTION_MOVING;
4238 #endif
4239     }
4240
4241     /* now make next step */
4242
4243     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4244
4245     if (DONT_COLLIDE_WITH(element) &&
4246         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4247         !PLAYER_PROTECTED(newx, newy))
4248     {
4249 #if 1
4250       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4251       return;
4252 #else
4253       /* player killed by element which is deadly when colliding with */
4254       MovDir[x][y] = 0;
4255       KillHero(PLAYERINFO(newx, newy));
4256       return;
4257 #endif
4258
4259     }
4260     else if ((element == EL_PENGUIN ||
4261               element == EL_ROBOT ||
4262               element == EL_SATELLITE ||
4263               element == EL_BALLOON ||
4264               IS_CUSTOM_ELEMENT(element)) &&
4265              IN_LEV_FIELD(newx, newy) &&
4266              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4267     {
4268       SplashAcid(x, y);
4269       Store[x][y] = EL_ACID;
4270     }
4271     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4272     {
4273       if (Feld[newx][newy] == EL_EXIT_OPEN)
4274       {
4275         Feld[x][y] = EL_EMPTY;
4276         DrawLevelField(x, y);
4277
4278         PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
4279         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4280           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4281
4282         local_player->friends_still_needed--;
4283         if (!local_player->friends_still_needed &&
4284             !local_player->GameOver && AllPlayersGone)
4285           local_player->LevelSolved = local_player->GameOver = TRUE;
4286
4287         return;
4288       }
4289       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4290       {
4291         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4292           DrawLevelField(newx, newy);
4293         else
4294           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4295       }
4296       else if (!IS_FREE(newx, newy))
4297       {
4298         GfxAction[x][y] = ACTION_WAITING;
4299
4300         if (IS_PLAYER(x, y))
4301           DrawPlayerField(x, y);
4302         else
4303           DrawLevelField(x, y);
4304         return;
4305       }
4306     }
4307     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4308     {
4309       if (IS_FOOD_PIG(Feld[newx][newy]))
4310       {
4311         if (IS_MOVING(newx, newy))
4312           RemoveMovingField(newx, newy);
4313         else
4314         {
4315           Feld[newx][newy] = EL_EMPTY;
4316           DrawLevelField(newx, newy);
4317         }
4318
4319         PlaySoundLevel(x, y, SND_PIG_DIGGING);
4320       }
4321       else if (!IS_FREE(newx, newy))
4322       {
4323         if (IS_PLAYER(x, y))
4324           DrawPlayerField(x, y);
4325         else
4326           DrawLevelField(x, y);
4327         return;
4328       }
4329     }
4330     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4331     {
4332       if (!IS_FREE(newx, newy))
4333       {
4334         if (IS_PLAYER(x, y))
4335           DrawPlayerField(x, y);
4336         else
4337           DrawLevelField(x, y);
4338
4339         return;
4340       }
4341       else
4342       {
4343         boolean wanna_flame = !RND(10);
4344         int dx = newx - x, dy = newy - y;
4345         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4346         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4347         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4348                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4349         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4350                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4351
4352         if ((wanna_flame ||
4353              IS_CLASSIC_ENEMY(element1) ||
4354              IS_CLASSIC_ENEMY(element2)) &&
4355             element1 != EL_DRAGON && element2 != EL_DRAGON &&
4356             element1 != EL_FLAMES && element2 != EL_FLAMES)
4357         {
4358 #if 1
4359           ResetGfxAnimation(x, y);
4360           GfxAction[x][y] = ACTION_ATTACKING;
4361 #endif
4362
4363           if (IS_PLAYER(x, y))
4364             DrawPlayerField(x, y);
4365           else
4366             DrawLevelField(x, y);
4367
4368           PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
4369
4370           MovDelay[x][y] = 50;
4371
4372           Feld[newx][newy] = EL_FLAMES;
4373           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
4374             Feld[newx1][newy1] = EL_FLAMES;
4375           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
4376             Feld[newx2][newy2] = EL_FLAMES;
4377
4378           return;
4379         }
4380       }
4381     }
4382     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4383              Feld[newx][newy] == EL_DIAMOND)
4384     {
4385       if (IS_MOVING(newx, newy))
4386         RemoveMovingField(newx, newy);
4387       else
4388       {
4389         Feld[newx][newy] = EL_EMPTY;
4390         DrawLevelField(newx, newy);
4391       }
4392
4393       PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
4394     }
4395     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
4396              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
4397     {
4398       if (AmoebaNr[newx][newy])
4399       {
4400         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4401         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4402             Feld[newx][newy] == EL_BD_AMOEBA)
4403           AmoebaCnt[AmoebaNr[newx][newy]]--;
4404       }
4405
4406       if (IS_MOVING(newx, newy))
4407         RemoveMovingField(newx, newy);
4408       else
4409       {
4410         Feld[newx][newy] = EL_EMPTY;
4411         DrawLevelField(newx, newy);
4412       }
4413
4414       PlaySoundLevel(x, y, SND_DARK_YAMYAM_DIGGING);
4415     }
4416     else if ((element == EL_PACMAN || element == EL_MOLE)
4417              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
4418     {
4419       if (AmoebaNr[newx][newy])
4420       {
4421         AmoebaCnt2[AmoebaNr[newx][newy]]--;
4422         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
4423             Feld[newx][newy] == EL_BD_AMOEBA)
4424           AmoebaCnt[AmoebaNr[newx][newy]]--;
4425       }
4426
4427       if (element == EL_MOLE)
4428       {
4429         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
4430         PlaySoundLevel(x, y, SND_MOLE_DIGGING);
4431
4432         ResetGfxAnimation(x, y);
4433         GfxAction[x][y] = ACTION_DIGGING;
4434         DrawLevelField(x, y);
4435
4436         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
4437         return;                         /* wait for shrinking amoeba */
4438       }
4439       else      /* element == EL_PACMAN */
4440       {
4441         Feld[newx][newy] = EL_EMPTY;
4442         DrawLevelField(newx, newy);
4443         PlaySoundLevel(x, y, SND_PACMAN_DIGGING);
4444       }
4445     }
4446     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
4447              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
4448               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
4449     {
4450       /* wait for shrinking amoeba to completely disappear */
4451       return;
4452     }
4453     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
4454     {
4455       /* object was running against a wall */
4456
4457       TurnRound(x, y);
4458
4459 #if 1
4460       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
4461         DrawLevelElementAnimation(x, y, element);
4462 #else
4463       if (element == EL_BUG ||
4464           element == EL_SPACESHIP ||
4465           element == EL_SP_SNIKSNAK)
4466         DrawLevelField(x, y);
4467       else if (element == EL_MOLE)
4468         DrawLevelField(x, y);
4469       else if (element == EL_BD_BUTTERFLY ||
4470                element == EL_BD_FIREFLY)
4471         DrawLevelElementAnimationIfNeeded(x, y, element);
4472       else if (element == EL_SATELLITE)
4473         DrawLevelElementAnimationIfNeeded(x, y, element);
4474       else if (element == EL_SP_ELECTRON)
4475         DrawLevelElementAnimationIfNeeded(x, y, element);
4476 #endif
4477
4478       if (DONT_TOUCH(element))
4479         TestIfBadThingTouchesHero(x, y);
4480
4481 #if 0
4482       PlaySoundLevelAction(x, y, ACTION_WAITING);
4483 #endif
4484
4485       return;
4486     }
4487
4488     InitMovingField(x, y, MovDir[x][y]);
4489
4490     PlaySoundLevelAction(x, y, ACTION_MOVING);
4491   }
4492
4493   if (MovDir[x][y])
4494     ContinueMoving(x, y);
4495 }
4496
4497 void ContinueMoving(int x, int y)
4498 {
4499   int element = Feld[x][y];
4500   int direction = MovDir[x][y];
4501   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4502   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4503   int newx = x + dx, newy = y + dy;
4504   int nextx = newx + dx, nexty = newy + dy;
4505   boolean pushed = Pushed[x][y];
4506
4507   MovPos[x][y] += getElementMoveStepsize(x, y);
4508
4509   if (pushed)           /* special case: moving object pushed by player */
4510     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
4511
4512   if (ABS(MovPos[x][y]) < TILEX)
4513   {
4514     DrawLevelField(x, y);
4515
4516     return;     /* element is still moving */
4517   }
4518
4519   /* element reached destination field */
4520
4521   Feld[x][y] = EL_EMPTY;
4522   Feld[newx][newy] = element;
4523   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
4524
4525   if (element == EL_MOLE)
4526   {
4527     Feld[x][y] = EL_SAND;
4528
4529     DrawLevelFieldCrumbledSandNeighbours(x, y);
4530   }
4531   else if (element == EL_QUICKSAND_FILLING)
4532   {
4533     element = Feld[newx][newy] = get_next_element(element);
4534     Store[newx][newy] = Store[x][y];
4535   }
4536   else if (element == EL_QUICKSAND_EMPTYING)
4537   {
4538     Feld[x][y] = get_next_element(element);
4539     element = Feld[newx][newy] = Store[x][y];
4540   }
4541   else if (element == EL_MAGIC_WALL_FILLING)
4542   {
4543     element = Feld[newx][newy] = get_next_element(element);
4544     if (!game.magic_wall_active)
4545       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
4546     Store[newx][newy] = Store[x][y];
4547   }
4548   else if (element == EL_MAGIC_WALL_EMPTYING)
4549   {
4550     Feld[x][y] = get_next_element(element);
4551     if (!game.magic_wall_active)
4552       Feld[x][y] = EL_MAGIC_WALL_DEAD;
4553     element = Feld[newx][newy] = Store[x][y];
4554   }
4555   else if (element == EL_BD_MAGIC_WALL_FILLING)
4556   {
4557     element = Feld[newx][newy] = get_next_element(element);
4558     if (!game.magic_wall_active)
4559       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
4560     Store[newx][newy] = Store[x][y];
4561   }
4562   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
4563   {
4564     Feld[x][y] = get_next_element(element);
4565     if (!game.magic_wall_active)
4566       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
4567     element = Feld[newx][newy] = Store[x][y];
4568   }
4569   else if (element == EL_AMOEBA_DROPPING)
4570   {
4571     Feld[x][y] = get_next_element(element);
4572     element = Feld[newx][newy] = Store[x][y];
4573   }
4574   else if (element == EL_SOKOBAN_OBJECT)
4575   {
4576     if (Back[x][y])
4577       Feld[x][y] = Back[x][y];
4578
4579     if (Back[newx][newy])
4580       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
4581
4582     Back[x][y] = Back[newx][newy] = 0;
4583   }
4584   else if (Store[x][y] == EL_ACID)
4585   {
4586     element = Feld[newx][newy] = EL_ACID;
4587   }
4588
4589   Store[x][y] = 0;
4590   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4591   MovDelay[newx][newy] = 0;
4592
4593   /* copy element change control values to new field */
4594   ChangeDelay[newx][newy] = ChangeDelay[x][y];
4595   ChangePage[newx][newy] = ChangePage[x][y];
4596   Changed[newx][newy] = Changed[x][y];
4597   ChangeEvent[newx][newy] = ChangeEvent[x][y];
4598
4599   ChangeDelay[x][y] = 0;
4600   ChangePage[x][y] = -1;
4601   Changed[x][y] = CE_BITMASK_DEFAULT;
4602   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
4603
4604   /* copy animation control values to new field */
4605   GfxFrame[newx][newy]  = GfxFrame[x][y];
4606   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
4607   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
4608   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
4609
4610   Pushed[x][y] = Pushed[newx][newy] = FALSE;
4611
4612   ResetGfxAnimation(x, y);      /* reset animation values for old field */
4613
4614 #if 0
4615   /* 2.1.1 (does not work correctly for spring) */
4616   if (!CAN_MOVE(element))
4617     MovDir[newx][newy] = 0;
4618 #else
4619
4620 #if 0
4621   /* (does not work for falling objects that slide horizontally) */
4622   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
4623     MovDir[newx][newy] = 0;
4624 #else
4625   /*
4626   if (!CAN_MOVE(element) ||
4627       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
4628     MovDir[newx][newy] = 0;
4629   */
4630
4631   if (!CAN_MOVE(element) ||
4632       (CAN_FALL(element) && direction == MV_DOWN))
4633     GfxDir[x][y] = MovDir[newx][newy] = 0;
4634
4635 #endif
4636 #endif
4637
4638   DrawLevelField(x, y);
4639   DrawLevelField(newx, newy);
4640
4641   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
4642
4643   /* prevent pushed element from moving on in pushed direction */
4644   if (pushed && CAN_MOVE(element) &&
4645       element_info[element].move_pattern & MV_ANY_DIRECTION &&
4646       !(element_info[element].move_pattern & direction))
4647     TurnRound(newx, newy);
4648
4649   if (!pushed)  /* special case: moving object pushed by player */
4650   {
4651     WasJustMoving[newx][newy] = 3;
4652
4653     if (CAN_FALL(element) && direction == MV_DOWN)
4654       WasJustFalling[newx][newy] = 3;
4655   }
4656
4657   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
4658   {
4659     TestIfBadThingTouchesHero(newx, newy);
4660     TestIfBadThingTouchesFriend(newx, newy);
4661     TestIfBadThingTouchesOtherBadThing(newx, newy);
4662   }
4663   else if (element == EL_PENGUIN)
4664     TestIfFriendTouchesBadThing(newx, newy);
4665
4666   if (CAN_FALL(element) && direction == MV_DOWN &&
4667       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
4668     Impact(x, newy);
4669
4670 #if 1
4671   TestIfElementTouchesCustomElement(x, y);              /* for empty space */
4672 #endif
4673
4674 #if 0
4675   if (ChangePage[newx][newy] != -1)                     /* delayed change */
4676     ChangeElement(newx, newy, ChangePage[newx][newy]);
4677 #endif
4678
4679   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
4680     CheckElementSideChange(newx, newy, Feld[newx][newy], direction,
4681                            CE_COLLISION, -1);
4682
4683   TestIfPlayerTouchesCustomElement(newx, newy);
4684   TestIfElementTouchesCustomElement(newx, newy);
4685 }
4686
4687 int AmoebeNachbarNr(int ax, int ay)
4688 {
4689   int i;
4690   int element = Feld[ax][ay];
4691   int group_nr = 0;
4692   static int xy[4][2] =
4693   {
4694     { 0, -1 },
4695     { -1, 0 },
4696     { +1, 0 },
4697     { 0, +1 }
4698   };
4699
4700   for (i=0; i<4; i++)
4701   {
4702     int x = ax + xy[i][0];
4703     int y = ay + xy[i][1];
4704
4705     if (!IN_LEV_FIELD(x, y))
4706       continue;
4707
4708     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
4709       group_nr = AmoebaNr[x][y];
4710   }
4711
4712   return group_nr;
4713 }
4714
4715 void AmoebenVereinigen(int ax, int ay)
4716 {
4717   int i, x, y, xx, yy;
4718   int new_group_nr = AmoebaNr[ax][ay];
4719   static int xy[4][2] =
4720   {
4721     { 0, -1 },
4722     { -1, 0 },
4723     { +1, 0 },
4724     { 0, +1 }
4725   };
4726
4727   if (new_group_nr == 0)
4728     return;
4729
4730   for (i=0; i<4; i++)
4731   {
4732     x = ax + xy[i][0];
4733     y = ay + xy[i][1];
4734
4735     if (!IN_LEV_FIELD(x, y))
4736       continue;
4737
4738     if ((Feld[x][y] == EL_AMOEBA_FULL ||
4739          Feld[x][y] == EL_BD_AMOEBA ||
4740          Feld[x][y] == EL_AMOEBA_DEAD) &&
4741         AmoebaNr[x][y] != new_group_nr)
4742     {
4743       int old_group_nr = AmoebaNr[x][y];
4744
4745       if (old_group_nr == 0)
4746         return;
4747
4748       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
4749       AmoebaCnt[old_group_nr] = 0;
4750       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
4751       AmoebaCnt2[old_group_nr] = 0;
4752
4753       for (yy=0; yy<lev_fieldy; yy++)
4754       {
4755         for (xx=0; xx<lev_fieldx; xx++)
4756         {
4757           if (AmoebaNr[xx][yy] == old_group_nr)
4758             AmoebaNr[xx][yy] = new_group_nr;
4759         }
4760       }
4761     }
4762   }
4763 }
4764
4765 void AmoebeUmwandeln(int ax, int ay)
4766 {
4767   int i, x, y;
4768
4769   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
4770   {
4771     int group_nr = AmoebaNr[ax][ay];
4772
4773 #ifdef DEBUG
4774     if (group_nr == 0)
4775     {
4776       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
4777       printf("AmoebeUmwandeln(): This should never happen!\n");
4778       return;
4779     }
4780 #endif
4781
4782     for (y=0; y<lev_fieldy; y++)
4783     {
4784       for (x=0; x<lev_fieldx; x++)
4785       {
4786         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
4787         {
4788           AmoebaNr[x][y] = 0;
4789           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
4790         }
4791       }
4792     }
4793     PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
4794                             SND_AMOEBA_TURNING_TO_GEM :
4795                             SND_AMOEBA_TURNING_TO_ROCK));
4796     Bang(ax, ay);
4797   }
4798   else
4799   {
4800     static int xy[4][2] =
4801     {
4802       { 0, -1 },
4803       { -1, 0 },
4804       { +1, 0 },
4805       { 0, +1 }
4806     };
4807
4808     for (i=0; i<4; i++)
4809     {
4810       x = ax + xy[i][0];
4811       y = ay + xy[i][1];
4812
4813       if (!IN_LEV_FIELD(x, y))
4814         continue;
4815
4816       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
4817       {
4818         PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
4819                               SND_AMOEBA_TURNING_TO_GEM :
4820                               SND_AMOEBA_TURNING_TO_ROCK));
4821         Bang(x, y);
4822       }
4823     }
4824   }
4825 }
4826
4827 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
4828 {
4829   int x, y;
4830   int group_nr = AmoebaNr[ax][ay];
4831   boolean done = FALSE;
4832
4833 #ifdef DEBUG
4834   if (group_nr == 0)
4835   {
4836     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
4837     printf("AmoebeUmwandelnBD(): This should never happen!\n");
4838     return;
4839   }
4840 #endif
4841
4842   for (y=0; y<lev_fieldy; y++)
4843   {
4844     for (x=0; x<lev_fieldx; x++)
4845     {
4846       if (AmoebaNr[x][y] == group_nr &&
4847           (Feld[x][y] == EL_AMOEBA_DEAD ||
4848            Feld[x][y] == EL_BD_AMOEBA ||
4849            Feld[x][y] == EL_AMOEBA_GROWING))
4850       {
4851         AmoebaNr[x][y] = 0;
4852         Feld[x][y] = new_element;
4853         InitField(x, y, FALSE);
4854         DrawLevelField(x, y);
4855         done = TRUE;
4856       }
4857     }
4858   }
4859
4860   if (done)
4861     PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
4862                             SND_BD_AMOEBA_TURNING_TO_ROCK :
4863                             SND_BD_AMOEBA_TURNING_TO_GEM));
4864 }
4865
4866 void AmoebeWaechst(int x, int y)
4867 {
4868   static unsigned long sound_delay = 0;
4869   static unsigned long sound_delay_value = 0;
4870
4871   if (!MovDelay[x][y])          /* start new growing cycle */
4872   {
4873     MovDelay[x][y] = 7;
4874
4875     if (DelayReached(&sound_delay, sound_delay_value))
4876     {
4877 #if 1
4878       PlaySoundLevelElementAction(x, y, Store[x][y], ACTION_GROWING);
4879 #else
4880       if (Store[x][y] == EL_BD_AMOEBA)
4881         PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
4882       else
4883         PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
4884 #endif
4885       sound_delay_value = 30;
4886     }
4887   }
4888
4889   if (MovDelay[x][y])           /* wait some time before growing bigger */
4890   {
4891     MovDelay[x][y]--;
4892     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4893     {
4894       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
4895                                            6 - MovDelay[x][y]);
4896
4897       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
4898     }
4899
4900     if (!MovDelay[x][y])
4901     {
4902       Feld[x][y] = Store[x][y];
4903       Store[x][y] = 0;
4904       DrawLevelField(x, y);
4905     }
4906   }
4907 }
4908
4909 void AmoebaDisappearing(int x, int y)
4910 {
4911   static unsigned long sound_delay = 0;
4912   static unsigned long sound_delay_value = 0;
4913
4914   if (!MovDelay[x][y])          /* start new shrinking cycle */
4915   {
4916     MovDelay[x][y] = 7;
4917
4918     if (DelayReached(&sound_delay, sound_delay_value))
4919       sound_delay_value = 30;
4920   }
4921
4922   if (MovDelay[x][y])           /* wait some time before shrinking */
4923   {
4924     MovDelay[x][y]--;
4925     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4926     {
4927       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
4928                                            6 - MovDelay[x][y]);
4929
4930       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
4931     }
4932
4933     if (!MovDelay[x][y])
4934     {
4935       Feld[x][y] = EL_EMPTY;
4936       DrawLevelField(x, y);
4937
4938       /* don't let mole enter this field in this cycle;
4939          (give priority to objects falling to this field from above) */
4940       Stop[x][y] = TRUE;
4941     }
4942   }
4943 }
4944
4945 void AmoebeAbleger(int ax, int ay)
4946 {
4947   int i;
4948   int element = Feld[ax][ay];
4949   int graphic = el2img(element);
4950   int newax = ax, neway = ay;
4951   static int xy[4][2] =
4952   {
4953     { 0, -1 },
4954     { -1, 0 },
4955     { +1, 0 },
4956     { 0, +1 }
4957   };
4958
4959   if (!level.amoeba_speed)
4960   {
4961     Feld[ax][ay] = EL_AMOEBA_DEAD;
4962     DrawLevelField(ax, ay);
4963     return;
4964   }
4965
4966   if (IS_ANIMATED(graphic))
4967     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
4968
4969   if (!MovDelay[ax][ay])        /* start making new amoeba field */
4970     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
4971
4972   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
4973   {
4974     MovDelay[ax][ay]--;
4975     if (MovDelay[ax][ay])
4976       return;
4977   }
4978
4979   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
4980   {
4981     int start = RND(4);
4982     int x = ax + xy[start][0];
4983     int y = ay + xy[start][1];
4984
4985     if (!IN_LEV_FIELD(x, y))
4986       return;
4987
4988     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
4989     if (IS_FREE(x, y) ||
4990         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
4991     {
4992       newax = x;
4993       neway = y;
4994     }
4995
4996     if (newax == ax && neway == ay)
4997       return;
4998   }
4999   else                          /* normal or "filled" (BD style) amoeba */
5000   {
5001     int start = RND(4);
5002     boolean waiting_for_player = FALSE;
5003
5004     for (i=0; i<4; i++)
5005     {
5006       int j = (start + i) % 4;
5007       int x = ax + xy[j][0];
5008       int y = ay + xy[j][1];
5009
5010       if (!IN_LEV_FIELD(x, y))
5011         continue;
5012
5013       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5014       if (IS_FREE(x, y) ||
5015           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5016       {
5017         newax = x;
5018         neway = y;
5019         break;
5020       }
5021       else if (IS_PLAYER(x, y))
5022         waiting_for_player = TRUE;
5023     }
5024
5025     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5026     {
5027       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5028       {
5029         Feld[ax][ay] = EL_AMOEBA_DEAD;
5030         DrawLevelField(ax, ay);
5031         AmoebaCnt[AmoebaNr[ax][ay]]--;
5032
5033         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5034         {
5035           if (element == EL_AMOEBA_FULL)
5036             AmoebeUmwandeln(ax, ay);
5037           else if (element == EL_BD_AMOEBA)
5038             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5039         }
5040       }
5041       return;
5042     }
5043     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5044     {
5045       /* amoeba gets larger by growing in some direction */
5046
5047       int new_group_nr = AmoebaNr[ax][ay];
5048
5049 #ifdef DEBUG
5050   if (new_group_nr == 0)
5051   {
5052     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5053     printf("AmoebeAbleger(): This should never happen!\n");
5054     return;
5055   }
5056 #endif
5057
5058       AmoebaNr[newax][neway] = new_group_nr;
5059       AmoebaCnt[new_group_nr]++;
5060       AmoebaCnt2[new_group_nr]++;
5061
5062       /* if amoeba touches other amoeba(s) after growing, unify them */
5063       AmoebenVereinigen(newax, neway);
5064
5065       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5066       {
5067         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5068         return;
5069       }
5070     }
5071   }
5072
5073   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5074       (neway == lev_fieldy - 1 && newax != ax))
5075   {
5076     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5077     Store[newax][neway] = element;
5078   }
5079   else if (neway == ay)
5080   {
5081     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5082 #if 1
5083     PlaySoundLevelAction(newax, neway, ACTION_GROWING);
5084 #else
5085     PlaySoundLevel(newax, neway, SND_AMOEBA_GROWING);
5086 #endif
5087   }
5088   else
5089   {
5090     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5091     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5092     Store[ax][ay] = EL_AMOEBA_DROP;
5093     ContinueMoving(ax, ay);
5094     return;
5095   }
5096
5097   DrawLevelField(newax, neway);
5098 }
5099
5100 void Life(int ax, int ay)
5101 {
5102   int x1, y1, x2, y2;
5103   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5104   int life_time = 40;
5105   int element = Feld[ax][ay];
5106   int graphic = el2img(element);
5107   boolean changed = FALSE;
5108
5109   if (IS_ANIMATED(graphic))
5110     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5111
5112   if (Stop[ax][ay])
5113     return;
5114
5115   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5116     MovDelay[ax][ay] = life_time;
5117
5118   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5119   {
5120     MovDelay[ax][ay]--;
5121     if (MovDelay[ax][ay])
5122       return;
5123   }
5124
5125   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
5126   {
5127     int xx = ax+x1, yy = ay+y1;
5128     int nachbarn = 0;
5129
5130     if (!IN_LEV_FIELD(xx, yy))
5131       continue;
5132
5133     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
5134     {
5135       int x = xx+x2, y = yy+y2;
5136
5137       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5138         continue;
5139
5140       if (((Feld[x][y] == element ||
5141             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5142            !Stop[x][y]) ||
5143           (IS_FREE(x, y) && Stop[x][y]))
5144         nachbarn++;
5145     }
5146
5147     if (xx == ax && yy == ay)           /* field in the middle */
5148     {
5149       if (nachbarn < life[0] || nachbarn > life[1])
5150       {
5151         Feld[xx][yy] = EL_EMPTY;
5152         if (!Stop[xx][yy])
5153           DrawLevelField(xx, yy);
5154         Stop[xx][yy] = TRUE;
5155         changed = TRUE;
5156       }
5157     }
5158     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5159     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5160     {                                   /* free border field */
5161       if (nachbarn >= life[2] && nachbarn <= life[3])
5162       {
5163         Feld[xx][yy] = element;
5164         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5165         if (!Stop[xx][yy])
5166           DrawLevelField(xx, yy);
5167         Stop[xx][yy] = TRUE;
5168         changed = TRUE;
5169       }
5170     }
5171   }
5172
5173   if (changed)
5174     PlaySoundLevel(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5175                    SND_GAME_OF_LIFE_GROWING);
5176 }
5177
5178 static void InitRobotWheel(int x, int y)
5179 {
5180   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5181 }
5182
5183 static void RunRobotWheel(int x, int y)
5184 {
5185   PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVE);
5186 }
5187
5188 static void StopRobotWheel(int x, int y)
5189 {
5190   if (ZX == x && ZY == y)
5191     ZX = ZY = -1;
5192 }
5193
5194 static void InitTimegateWheel(int x, int y)
5195 {
5196   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5197 }
5198
5199 static void RunTimegateWheel(int x, int y)
5200 {
5201   PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5202 }
5203
5204 void CheckExit(int x, int y)
5205 {
5206   if (local_player->gems_still_needed > 0 ||
5207       local_player->sokobanfields_still_needed > 0 ||
5208       local_player->lights_still_needed > 0)
5209   {
5210     int element = Feld[x][y];
5211     int graphic = el2img(element);
5212
5213     if (IS_ANIMATED(graphic))
5214       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5215
5216     return;
5217   }
5218
5219   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5220     return;
5221
5222   Feld[x][y] = EL_EXIT_OPENING;
5223
5224   PlaySoundLevelNearest(x, y, SND_CLASS_EXIT_OPENING);
5225 }
5226
5227 void CheckExitSP(int x, int y)
5228 {
5229   if (local_player->gems_still_needed > 0)
5230   {
5231     int element = Feld[x][y];
5232     int graphic = el2img(element);
5233
5234     if (IS_ANIMATED(graphic))
5235       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5236
5237     return;
5238   }
5239
5240   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5241     return;
5242
5243   Feld[x][y] = EL_SP_EXIT_OPENING;
5244
5245   PlaySoundLevelNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5246 }
5247
5248 static void CloseAllOpenTimegates()
5249 {
5250   int x, y;
5251
5252   for (y=0; y<lev_fieldy; y++)
5253   {
5254     for (x=0; x<lev_fieldx; x++)
5255     {
5256       int element = Feld[x][y];
5257
5258       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5259       {
5260         Feld[x][y] = EL_TIMEGATE_CLOSING;
5261 #if 1
5262         PlaySoundLevelAction(x, y, ACTION_CLOSING);
5263 #else
5264         PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
5265 #endif
5266       }
5267     }
5268   }
5269 }
5270
5271 void EdelsteinFunkeln(int x, int y)
5272 {
5273   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
5274     return;
5275
5276   if (Feld[x][y] == EL_BD_DIAMOND)
5277     return;
5278
5279   if (MovDelay[x][y] == 0)      /* next animation frame */
5280     MovDelay[x][y] = 11 * !SimpleRND(500);
5281
5282   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
5283   {
5284     MovDelay[x][y]--;
5285
5286     if (setup.direct_draw && MovDelay[x][y])
5287       SetDrawtoField(DRAW_BUFFERED);
5288
5289     DrawLevelElementAnimation(x, y, Feld[x][y]);
5290
5291     if (MovDelay[x][y] != 0)
5292     {
5293       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
5294                                            10 - MovDelay[x][y]);
5295
5296       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
5297
5298       if (setup.direct_draw)
5299       {
5300         int dest_x, dest_y;
5301
5302         dest_x = FX + SCREENX(x) * TILEX;
5303         dest_y = FY + SCREENY(y) * TILEY;
5304
5305         BlitBitmap(drawto_field, window,
5306                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
5307         SetDrawtoField(DRAW_DIRECT);
5308       }
5309     }
5310   }
5311 }
5312
5313 void MauerWaechst(int x, int y)
5314 {
5315   int delay = 6;
5316
5317   if (!MovDelay[x][y])          /* next animation frame */
5318     MovDelay[x][y] = 3 * delay;
5319
5320   if (MovDelay[x][y])           /* wait some time before next frame */
5321   {
5322     MovDelay[x][y]--;
5323
5324     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5325     {
5326       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
5327       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
5328
5329       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5330     }
5331
5332     if (!MovDelay[x][y])
5333     {
5334       if (MovDir[x][y] == MV_LEFT)
5335       {
5336         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
5337           DrawLevelField(x - 1, y);
5338       }
5339       else if (MovDir[x][y] == MV_RIGHT)
5340       {
5341         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
5342           DrawLevelField(x + 1, y);
5343       }
5344       else if (MovDir[x][y] == MV_UP)
5345       {
5346         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
5347           DrawLevelField(x, y - 1);
5348       }
5349       else
5350       {
5351         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
5352           DrawLevelField(x, y + 1);
5353       }
5354
5355       Feld[x][y] = Store[x][y];
5356       Store[x][y] = 0;
5357       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5358       DrawLevelField(x, y);
5359     }
5360   }
5361 }
5362
5363 void MauerAbleger(int ax, int ay)
5364 {
5365   int element = Feld[ax][ay];
5366   int graphic = el2img(element);
5367   boolean oben_frei = FALSE, unten_frei = FALSE;
5368   boolean links_frei = FALSE, rechts_frei = FALSE;
5369   boolean oben_massiv = FALSE, unten_massiv = FALSE;
5370   boolean links_massiv = FALSE, rechts_massiv = FALSE;
5371   boolean new_wall = FALSE;
5372
5373   if (IS_ANIMATED(graphic))
5374     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5375
5376   if (!MovDelay[ax][ay])        /* start building new wall */
5377     MovDelay[ax][ay] = 6;
5378
5379   if (MovDelay[ax][ay])         /* wait some time before building new wall */
5380   {
5381     MovDelay[ax][ay]--;
5382     if (MovDelay[ax][ay])
5383       return;
5384   }
5385
5386   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
5387     oben_frei = TRUE;
5388   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
5389     unten_frei = TRUE;
5390   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
5391     links_frei = TRUE;
5392   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
5393     rechts_frei = TRUE;
5394
5395   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
5396       element == EL_EXPANDABLE_WALL_ANY)
5397   {
5398     if (oben_frei)
5399     {
5400       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
5401       Store[ax][ay-1] = element;
5402       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
5403       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
5404         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
5405                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
5406       new_wall = TRUE;
5407     }
5408     if (unten_frei)
5409     {
5410       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
5411       Store[ax][ay+1] = element;
5412       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
5413       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
5414         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
5415                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
5416       new_wall = TRUE;
5417     }
5418   }
5419
5420   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5421       element == EL_EXPANDABLE_WALL_ANY ||
5422       element == EL_EXPANDABLE_WALL)
5423   {
5424     if (links_frei)
5425     {
5426       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
5427       Store[ax-1][ay] = element;
5428       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
5429       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
5430         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
5431                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
5432       new_wall = TRUE;
5433     }
5434
5435     if (rechts_frei)
5436     {
5437       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
5438       Store[ax+1][ay] = element;
5439       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
5440       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
5441         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
5442                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
5443       new_wall = TRUE;
5444     }
5445   }
5446
5447   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
5448     DrawLevelField(ax, ay);
5449
5450   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
5451     oben_massiv = TRUE;
5452   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
5453     unten_massiv = TRUE;
5454   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
5455     links_massiv = TRUE;
5456   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
5457     rechts_massiv = TRUE;
5458
5459   if (((oben_massiv && unten_massiv) ||
5460        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5461        element == EL_EXPANDABLE_WALL) &&
5462       ((links_massiv && rechts_massiv) ||
5463        element == EL_EXPANDABLE_WALL_VERTICAL))
5464     Feld[ax][ay] = EL_WALL;
5465
5466   if (new_wall)
5467 #if 1
5468     PlaySoundLevelAction(ax, ay, ACTION_GROWING);
5469 #else
5470     PlaySoundLevel(ax, ay, SND_EXPANDABLE_WALL_GROWING);
5471 #endif
5472 }
5473
5474 void CheckForDragon(int x, int y)
5475 {
5476   int i, j;
5477   boolean dragon_found = FALSE;
5478   static int xy[4][2] =
5479   {
5480     { 0, -1 },
5481     { -1, 0 },
5482     { +1, 0 },
5483     { 0, +1 }
5484   };
5485
5486   for (i=0; i<4; i++)
5487   {
5488     for (j=0; j<4; j++)
5489     {
5490       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5491
5492       if (IN_LEV_FIELD(xx, yy) &&
5493           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
5494       {
5495         if (Feld[xx][yy] == EL_DRAGON)
5496           dragon_found = TRUE;
5497       }
5498       else
5499         break;
5500     }
5501   }
5502
5503   if (!dragon_found)
5504   {
5505     for (i=0; i<4; i++)
5506     {
5507       for (j=0; j<3; j++)
5508       {
5509         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
5510   
5511         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
5512         {
5513           Feld[xx][yy] = EL_EMPTY;
5514           DrawLevelField(xx, yy);
5515         }
5516         else
5517           break;
5518       }
5519     }
5520   }
5521 }
5522
5523 static void InitBuggyBase(int x, int y)
5524 {
5525   int element = Feld[x][y];
5526   int activating_delay = FRAMES_PER_SECOND / 4;
5527
5528   ChangeDelay[x][y] =
5529     (element == EL_SP_BUGGY_BASE ?
5530      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
5531      element == EL_SP_BUGGY_BASE_ACTIVATING ?
5532      activating_delay :
5533      element == EL_SP_BUGGY_BASE_ACTIVE ?
5534      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
5535 }
5536
5537 static void WarnBuggyBase(int x, int y)
5538 {
5539   int i;
5540   static int xy[4][2] =
5541   {
5542     { 0, -1 },
5543     { -1, 0 },
5544     { +1, 0 },
5545     { 0, +1 }
5546   };
5547
5548   for (i=0; i<4; i++)
5549   {
5550     int xx = x + xy[i][0], yy = y + xy[i][1];
5551
5552     if (IS_PLAYER(xx, yy))
5553     {
5554       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVE);
5555
5556       break;
5557     }
5558   }
5559 }
5560
5561 static void InitTrap(int x, int y)
5562 {
5563   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
5564 }
5565
5566 static void ActivateTrap(int x, int y)
5567 {
5568   PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
5569 }
5570
5571 static void ChangeActiveTrap(int x, int y)
5572 {
5573   int graphic = IMG_TRAP_ACTIVE;
5574
5575   /* if new animation frame was drawn, correct crumbled sand border */
5576   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
5577     DrawLevelFieldCrumbledSand(x, y);
5578 }
5579
5580 static void ChangeElementNowExt(int x, int y, int target_element)
5581 {
5582   /* check if element under player changes from accessible to unaccessible
5583      (needed for special case of dropping element which then changes) */
5584   if (IS_PLAYER(x, y) && !PLAYER_PROTECTED(x, y) &&
5585       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
5586   {
5587     Bang(x, y);
5588     return;
5589   }
5590
5591   RemoveField(x, y);
5592   Feld[x][y] = target_element;
5593
5594   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5595
5596   ResetGfxAnimation(x, y);
5597   ResetRandomAnimationValue(x, y);
5598
5599   InitField(x, y, FALSE);
5600   if (CAN_MOVE(Feld[x][y]))
5601     InitMovDir(x, y);
5602
5603   DrawLevelField(x, y);
5604
5605   if (GFX_CRUMBLED(Feld[x][y]))
5606     DrawLevelFieldCrumbledSandNeighbours(x, y);
5607
5608   TestIfBadThingTouchesHero(x, y);
5609   TestIfPlayerTouchesCustomElement(x, y);
5610   TestIfElementTouchesCustomElement(x, y);
5611
5612   if (ELEM_IS_PLAYER(target_element))
5613     RelocatePlayer(x, y, target_element);
5614 }
5615
5616 static boolean ChangeElementNow(int x, int y, int element, int page)
5617 {
5618   struct ElementChangeInfo *change = &element_info[element].change_page[page];
5619
5620   /* always use default change event to prevent running into a loop */
5621   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
5622     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
5623
5624   /* do not change already changed elements with same change event */
5625 #if 0
5626   if (Changed[x][y] & ChangeEvent[x][y])
5627     return FALSE;
5628 #else
5629   if (Changed[x][y])
5630     return FALSE;
5631 #endif
5632
5633   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
5634
5635   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5636
5637   if (change->explode)
5638   {
5639     Bang(x, y);
5640
5641     return TRUE;
5642   }
5643
5644   if (change->use_content)
5645   {
5646     boolean complete_change = TRUE;
5647     boolean can_change[3][3];
5648     int xx, yy;
5649
5650     for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5651     {
5652       boolean half_destructible;
5653       int ex = x + xx - 1;
5654       int ey = y + yy - 1;
5655       int e;
5656
5657       can_change[xx][yy] = TRUE;
5658
5659       if (ex == x && ey == y)   /* do not check changing element itself */
5660         continue;
5661
5662       if (change->content[xx][yy] == EL_EMPTY_SPACE)
5663       {
5664         can_change[xx][yy] = FALSE;     /* do not change empty borders */
5665
5666         continue;
5667       }
5668
5669       if (!IN_LEV_FIELD(ex, ey))
5670       {
5671         can_change[xx][yy] = FALSE;
5672         complete_change = FALSE;
5673
5674         continue;
5675       }
5676
5677       e = Feld[ex][ey];
5678
5679       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5680         e = MovingOrBlocked2Element(ex, ey);
5681
5682       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5683
5684       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
5685           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5686           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5687       {
5688         can_change[xx][yy] = FALSE;
5689         complete_change = FALSE;
5690       }
5691     }
5692
5693     if (!change->only_complete || complete_change)
5694     {
5695       boolean something_has_changed = FALSE;
5696
5697       if (change->only_complete && change->use_random_change &&
5698           RND(100) < change->random)
5699         return FALSE;
5700
5701       for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5702       {
5703         int ex = x + xx - 1;
5704         int ey = y + yy - 1;
5705
5706         if (can_change[xx][yy] && (!change->use_random_change ||
5707                                    RND(100) < change->random))
5708         {
5709           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5710             RemoveMovingField(ex, ey);
5711
5712           ChangeEvent[ex][ey] = ChangeEvent[x][y];
5713
5714           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5715
5716           something_has_changed = TRUE;
5717
5718           /* for symmetry reasons, freeze newly created border elements */
5719           if (ex != x || ey != y)
5720             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
5721         }
5722       }
5723
5724       if (something_has_changed)
5725         PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5726     }
5727   }
5728   else
5729   {
5730     ChangeElementNowExt(x, y, change->target_element);
5731
5732     PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5733   }
5734
5735   return TRUE;
5736 }
5737
5738 static void ChangeElement(int x, int y, int page)
5739 {
5740   int element = MovingOrBlocked2Element(x, y);
5741   struct ElementInfo *ei = &element_info[element];
5742   struct ElementChangeInfo *change = &ei->change_page[page];
5743
5744 #if 0
5745 #ifdef DEBUG
5746   if (!CAN_CHANGE(element))
5747   {
5748     printf("\n\n");
5749     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
5750            x, y, element, element_info[element].token_name);
5751     printf("ChangeElement(): This should never happen!\n");
5752     printf("\n\n");
5753   }
5754 #endif
5755 #endif
5756
5757   if (ChangeDelay[x][y] == 0)           /* initialize element change */
5758   {
5759     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
5760                          RND(change->delay_random * change->delay_frames)) + 1;
5761
5762     ResetGfxAnimation(x, y);
5763     ResetRandomAnimationValue(x, y);
5764
5765     if (change->pre_change_function)
5766       change->pre_change_function(x, y);
5767   }
5768
5769   ChangeDelay[x][y]--;
5770
5771   if (ChangeDelay[x][y] != 0)           /* continue element change */
5772   {
5773     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5774
5775     if (IS_ANIMATED(graphic))
5776       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5777
5778     if (change->change_function)
5779       change->change_function(x, y);
5780   }
5781   else                                  /* finish element change */
5782   {
5783     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
5784     {
5785       page = ChangePage[x][y];
5786       ChangePage[x][y] = -1;
5787     }
5788
5789     if (IS_MOVING(x, y))                /* never change a running system ;-) */
5790     {
5791       ChangeDelay[x][y] = 1;            /* try change after next move step */
5792       ChangePage[x][y] = page;          /* remember page to use for change */
5793
5794       return;
5795     }
5796
5797     if (ChangeElementNow(x, y, element, page))
5798     {
5799       if (change->post_change_function)
5800         change->post_change_function(x, y);
5801     }
5802   }
5803 }
5804
5805 static boolean CheckTriggeredElementSideChange(int lx, int ly,
5806                                                int trigger_element,
5807                                                int trigger_side,
5808                                                int trigger_event)
5809 {
5810   int i, j, x, y;
5811
5812   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5813     return FALSE;
5814
5815   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
5816   {
5817     int element = EL_CUSTOM_START + i;
5818
5819     boolean change_element = FALSE;
5820     int page = 0;
5821
5822     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5823       continue;
5824
5825     for (j=0; j < element_info[element].num_change_pages; j++)
5826     {
5827       struct ElementChangeInfo *change = &element_info[element].change_page[j];
5828
5829       if (change->can_change &&
5830 #if 1
5831           change->events & CH_EVENT_BIT(trigger_event) &&
5832 #endif
5833           change->sides & trigger_side &&
5834           change->trigger_element == trigger_element)
5835       {
5836 #if 0
5837         if (!(change->events & CH_EVENT_BIT(trigger_event)))
5838           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
5839                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
5840 #endif
5841
5842         change_element = TRUE;
5843         page = j;
5844
5845         break;
5846       }
5847     }
5848
5849     if (!change_element)
5850       continue;
5851
5852     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5853     {
5854 #if 0
5855       if (x == lx && y == ly)   /* do not change trigger element itself */
5856         continue;
5857 #endif
5858
5859       if (Feld[x][y] == element)
5860       {
5861         ChangeDelay[x][y] = 1;
5862         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5863         ChangeElement(x, y, page);
5864       }
5865     }
5866   }
5867
5868   return TRUE;
5869 }
5870
5871 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5872                                            int trigger_event)
5873 {
5874   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
5875                                          trigger_event);
5876 }
5877
5878 static boolean CheckElementSideChange(int x, int y, int element, int side,
5879                                       int trigger_event, int page)
5880 {
5881   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
5882     return FALSE;
5883
5884   if (Feld[x][y] == EL_BLOCKED)
5885   {
5886     Blocked2Moving(x, y, &x, &y);
5887     element = Feld[x][y];
5888   }
5889
5890   if (page < 0)
5891     page = element_info[element].event_page_nr[trigger_event];
5892
5893   if (!(element_info[element].change_page[page].sides & side))
5894     return FALSE;
5895
5896   ChangeDelay[x][y] = 1;
5897   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
5898   ChangeElement(x, y, page);
5899
5900   return TRUE;
5901 }
5902
5903 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5904 {
5905   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
5906 }
5907
5908 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5909 {
5910   static byte stored_player_action[MAX_PLAYERS];
5911   static int num_stored_actions = 0;
5912   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5913   int left      = player_action & JOY_LEFT;
5914   int right     = player_action & JOY_RIGHT;
5915   int up        = player_action & JOY_UP;
5916   int down      = player_action & JOY_DOWN;
5917   int button1   = player_action & JOY_BUTTON_1;
5918   int button2   = player_action & JOY_BUTTON_2;
5919   int dx        = (left ? -1    : right ? 1     : 0);
5920   int dy        = (up   ? -1    : down  ? 1     : 0);
5921
5922   stored_player_action[player->index_nr] = 0;
5923   num_stored_actions++;
5924
5925   if (!player->active || tape.pausing)
5926     return;
5927
5928   if (player_action)
5929   {
5930     if (button1)
5931       snapped = SnapField(player, dx, dy);
5932     else
5933     {
5934       if (button2)
5935         dropped = DropElement(player);
5936
5937       moved = MovePlayer(player, dx, dy);
5938     }
5939
5940     if (tape.single_step && tape.recording && !tape.pausing)
5941     {
5942       if (button1 || (dropped && !moved))
5943       {
5944         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5945         SnapField(player, 0, 0);                /* stop snapping */
5946       }
5947     }
5948
5949     stored_player_action[player->index_nr] = player_action;
5950   }
5951   else
5952   {
5953     /* no actions for this player (no input at player's configured device) */
5954
5955     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5956     SnapField(player, 0, 0);
5957     CheckGravityMovement(player);
5958
5959     if (player->MovPos == 0)
5960       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5961
5962     if (player->MovPos == 0)    /* needed for tape.playing */
5963       player->is_moving = FALSE;
5964   }
5965
5966   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5967   {
5968     TapeRecordAction(stored_player_action);
5969     num_stored_actions = 0;
5970   }
5971 }
5972
5973 void GameActions()
5974 {
5975   static unsigned long action_delay = 0;
5976   unsigned long action_delay_value;
5977   int magic_wall_x = 0, magic_wall_y = 0;
5978   int i, x, y, element, graphic;
5979   byte *recorded_player_action;
5980   byte summarized_player_action = 0;
5981
5982   if (game_status != GAME_MODE_PLAYING)
5983     return;
5984
5985   action_delay_value =
5986     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5987
5988   if (tape.playing && tape.index_search && !tape.pausing)
5989     action_delay_value = 0;
5990
5991   /* ---------- main game synchronization point ---------- */
5992
5993   WaitUntilDelayReached(&action_delay, action_delay_value);
5994
5995   if (network_playing && !network_player_action_received)
5996   {
5997     /*
5998 #ifdef DEBUG
5999     printf("DEBUG: try to get network player actions in time\n");
6000 #endif
6001     */
6002
6003 #if defined(PLATFORM_UNIX)
6004     /* last chance to get network player actions without main loop delay */
6005     HandleNetworking();
6006 #endif
6007
6008     if (game_status != GAME_MODE_PLAYING)
6009       return;
6010
6011     if (!network_player_action_received)
6012     {
6013       /*
6014 #ifdef DEBUG
6015       printf("DEBUG: failed to get network player actions in time\n");
6016 #endif
6017       */
6018       return;
6019     }
6020   }
6021
6022   if (tape.pausing)
6023     return;
6024
6025   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
6026
6027   for (i=0; i<MAX_PLAYERS; i++)
6028   {
6029     summarized_player_action |= stored_player[i].action;
6030
6031     if (!network_playing)
6032       stored_player[i].effective_action = stored_player[i].action;
6033   }
6034
6035 #if defined(PLATFORM_UNIX)
6036   if (network_playing)
6037     SendToServer_MovePlayer(summarized_player_action);
6038 #endif
6039
6040   if (!options.network && !setup.team_mode)
6041     local_player->effective_action = summarized_player_action;
6042
6043   for (i=0; i<MAX_PLAYERS; i++)
6044   {
6045     int actual_player_action = stored_player[i].effective_action;
6046
6047     if (stored_player[i].programmed_action)
6048       actual_player_action = stored_player[i].programmed_action;
6049
6050     if (recorded_player_action)
6051       actual_player_action = recorded_player_action[i];
6052
6053     PlayerActions(&stored_player[i], actual_player_action);
6054     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
6055   }
6056
6057   network_player_action_received = FALSE;
6058
6059   ScrollScreen(NULL, SCROLL_GO_ON);
6060
6061 #if 0
6062   FrameCounter++;
6063   TimeFrames++;
6064
6065   for (i=0; i<MAX_PLAYERS; i++)
6066     stored_player[i].Frame++;
6067 #endif
6068
6069 #if 1
6070   if (game.engine_version < VERSION_IDENT(2,2,0,7))
6071   {
6072     for (i=0; i<MAX_PLAYERS; i++)
6073     {
6074       struct PlayerInfo *player = &stored_player[i];
6075       int x = player->jx;
6076       int y = player->jy;
6077
6078       if (player->active && player->is_pushing && player->is_moving &&
6079           IS_MOVING(x, y))
6080       {
6081         ContinueMoving(x, y);
6082
6083         /* continue moving after pushing (this is actually a bug) */
6084         if (!IS_MOVING(x, y))
6085         {
6086           Stop[x][y] = FALSE;
6087         }
6088       }
6089     }
6090   }
6091 #endif
6092
6093   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6094   {
6095     Changed[x][y] = CE_BITMASK_DEFAULT;
6096     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6097
6098 #if DEBUG
6099     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
6100     {
6101       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
6102       printf("GameActions(): This should never happen!\n");
6103
6104       ChangePage[x][y] = -1;
6105     }
6106 #endif
6107
6108     Stop[x][y] = FALSE;
6109     if (WasJustMoving[x][y] > 0)
6110       WasJustMoving[x][y]--;
6111     if (WasJustFalling[x][y] > 0)
6112       WasJustFalling[x][y]--;
6113
6114     GfxFrame[x][y]++;
6115
6116 #if 1
6117     /* reset finished pushing action (not done in ContinueMoving() to allow
6118        continous pushing animation for elements with zero push delay) */
6119     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
6120     {
6121       ResetGfxAnimation(x, y);
6122       DrawLevelField(x, y);
6123     }
6124 #endif
6125
6126 #if DEBUG
6127     if (IS_BLOCKED(x, y))
6128     {
6129       int oldx, oldy;
6130
6131       Blocked2Moving(x, y, &oldx, &oldy);
6132       if (!IS_MOVING(oldx, oldy))
6133       {
6134         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
6135         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
6136         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
6137         printf("GameActions(): This should never happen!\n");
6138       }
6139     }
6140 #endif
6141   }
6142
6143   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6144   {
6145     element = Feld[x][y];
6146 #if 1
6147     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6148 #else
6149     graphic = el2img(element);
6150 #endif
6151
6152 #if 0
6153     if (element == -1)
6154     {
6155       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
6156
6157       element = graphic = 0;
6158     }
6159 #endif
6160
6161     if (graphic_info[graphic].anim_global_sync)
6162       GfxFrame[x][y] = FrameCounter;
6163
6164     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
6165         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
6166       ResetRandomAnimationValue(x, y);
6167
6168     SetRandomAnimationValue(x, y);
6169
6170 #if 1
6171     PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
6172 #endif
6173
6174     if (IS_INACTIVE(element))
6175     {
6176       if (IS_ANIMATED(graphic))
6177         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6178
6179       continue;
6180     }
6181
6182 #if 1
6183     /* this may take place after moving, so 'element' may have changed */
6184 #if 0
6185     if (IS_CHANGING(x, y))
6186 #else
6187     if (IS_CHANGING(x, y) &&
6188         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
6189 #endif
6190     {
6191 #if 0
6192       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
6193                     element_info[element].event_page_nr[CE_DELAY]);
6194 #else
6195       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
6196 #endif
6197
6198       element = Feld[x][y];
6199       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6200     }
6201 #endif
6202
6203     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
6204     {
6205       StartMoving(x, y);
6206
6207 #if 1
6208       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6209 #if 0
6210       if (element == EL_MOLE)
6211         printf("::: %d, %d, %d [%d]\n",
6212                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
6213                GfxAction[x][y]);
6214 #endif
6215 #if 0
6216       if (element == EL_YAMYAM)
6217         printf("::: %d, %d, %d\n",
6218                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
6219 #endif
6220 #endif
6221
6222       if (IS_ANIMATED(graphic) &&
6223           !IS_MOVING(x, y) &&
6224           !Stop[x][y])
6225       {
6226         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6227
6228 #if 0
6229         if (element == EL_MOLE)
6230           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
6231 #endif
6232       }
6233
6234       if (IS_GEM(element) || element == EL_SP_INFOTRON)
6235         EdelsteinFunkeln(x, y);
6236     }
6237     else if ((element == EL_ACID ||
6238               element == EL_EXIT_OPEN ||
6239               element == EL_SP_EXIT_OPEN ||
6240               element == EL_SP_TERMINAL ||
6241               element == EL_SP_TERMINAL_ACTIVE ||
6242               element == EL_EXTRA_TIME ||
6243               element == EL_SHIELD_NORMAL ||
6244               element == EL_SHIELD_DEADLY) &&
6245              IS_ANIMATED(graphic))
6246       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6247     else if (IS_MOVING(x, y))
6248       ContinueMoving(x, y);
6249     else if (IS_ACTIVE_BOMB(element))
6250       CheckDynamite(x, y);
6251 #if 0
6252     else if (element == EL_EXPLOSION && !game.explosions_delayed)
6253       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6254 #endif
6255     else if (element == EL_AMOEBA_GROWING)
6256       AmoebeWaechst(x, y);
6257     else if (element == EL_AMOEBA_SHRINKING)
6258       AmoebaDisappearing(x, y);
6259
6260 #if !USE_NEW_AMOEBA_CODE
6261     else if (IS_AMOEBALIVE(element))
6262       AmoebeAbleger(x, y);
6263 #endif
6264
6265     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
6266       Life(x, y);
6267     else if (element == EL_EXIT_CLOSED)
6268       CheckExit(x, y);
6269     else if (element == EL_SP_EXIT_CLOSED)
6270       CheckExitSP(x, y);
6271     else if (element == EL_EXPANDABLE_WALL_GROWING)
6272       MauerWaechst(x, y);
6273     else if (element == EL_EXPANDABLE_WALL ||
6274              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6275              element == EL_EXPANDABLE_WALL_VERTICAL ||
6276              element == EL_EXPANDABLE_WALL_ANY)
6277       MauerAbleger(x, y);
6278     else if (element == EL_FLAMES)
6279       CheckForDragon(x, y);
6280 #if 0
6281     else if (IS_AUTO_CHANGING(element))
6282       ChangeElement(x, y);
6283 #endif
6284     else if (element == EL_EXPLOSION)
6285       ; /* drawing of correct explosion animation is handled separately */
6286     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
6287       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6288
6289 #if 0
6290     /* this may take place after moving, so 'element' may have changed */
6291     if (IS_AUTO_CHANGING(Feld[x][y]))
6292       ChangeElement(x, y);
6293 #endif
6294
6295     if (IS_BELT_ACTIVE(element))
6296       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
6297
6298     if (game.magic_wall_active)
6299     {
6300       int jx = local_player->jx, jy = local_player->jy;
6301
6302       /* play the element sound at the position nearest to the player */
6303       if ((element == EL_MAGIC_WALL_FULL ||
6304            element == EL_MAGIC_WALL_ACTIVE ||
6305            element == EL_MAGIC_WALL_EMPTYING ||
6306            element == EL_BD_MAGIC_WALL_FULL ||
6307            element == EL_BD_MAGIC_WALL_ACTIVE ||
6308            element == EL_BD_MAGIC_WALL_EMPTYING) &&
6309           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
6310       {
6311         magic_wall_x = x;
6312         magic_wall_y = y;
6313       }
6314     }
6315   }
6316
6317 #if USE_NEW_AMOEBA_CODE
6318   /* new experimental amoeba growth stuff */
6319 #if 1
6320   if (!(FrameCounter % 8))
6321 #endif
6322   {
6323     static unsigned long random = 1684108901;
6324
6325     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
6326     {
6327 #if 0
6328       x = (random >> 10) % lev_fieldx;
6329       y = (random >> 20) % lev_fieldy;
6330 #else
6331       x = RND(lev_fieldx);
6332       y = RND(lev_fieldy);
6333 #endif
6334       element = Feld[x][y];
6335
6336       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6337       if (!IS_PLAYER(x,y) &&
6338           (element == EL_EMPTY ||
6339            element == EL_SAND ||
6340            element == EL_QUICKSAND_EMPTY ||
6341            element == EL_ACID_SPLASH_LEFT ||
6342            element == EL_ACID_SPLASH_RIGHT))
6343       {
6344         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6345             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6346             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6347             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6348           Feld[x][y] = EL_AMOEBA_DROP;
6349       }
6350
6351       random = random * 129 + 1;
6352     }
6353   }
6354 #endif
6355
6356 #if 0
6357   if (game.explosions_delayed)
6358 #endif
6359   {
6360     game.explosions_delayed = FALSE;
6361
6362     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6363     {
6364       element = Feld[x][y];
6365
6366       if (ExplodeField[x][y])
6367         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6368       else if (element == EL_EXPLOSION)
6369         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6370
6371       ExplodeField[x][y] = EX_NO_EXPLOSION;
6372     }
6373
6374     game.explosions_delayed = TRUE;
6375   }
6376
6377   if (game.magic_wall_active)
6378   {
6379     if (!(game.magic_wall_time_left % 4))
6380     {
6381       int element = Feld[magic_wall_x][magic_wall_y];
6382
6383       if (element == EL_BD_MAGIC_WALL_FULL ||
6384           element == EL_BD_MAGIC_WALL_ACTIVE ||
6385           element == EL_BD_MAGIC_WALL_EMPTYING)
6386         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6387       else
6388         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6389     }
6390
6391     if (game.magic_wall_time_left > 0)
6392     {
6393       game.magic_wall_time_left--;
6394       if (!game.magic_wall_time_left)
6395       {
6396         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6397         {
6398           element = Feld[x][y];
6399
6400           if (element == EL_MAGIC_WALL_ACTIVE ||
6401               element == EL_MAGIC_WALL_FULL)
6402           {
6403             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6404             DrawLevelField(x, y);
6405           }
6406           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6407                    element == EL_BD_MAGIC_WALL_FULL)
6408           {
6409             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6410             DrawLevelField(x, y);
6411           }
6412         }
6413
6414         game.magic_wall_active = FALSE;
6415       }
6416     }
6417   }
6418
6419   if (game.light_time_left > 0)
6420   {
6421     game.light_time_left--;
6422
6423     if (game.light_time_left == 0)
6424       RedrawAllLightSwitchesAndInvisibleElements();
6425   }
6426
6427   if (game.timegate_time_left > 0)
6428   {
6429     game.timegate_time_left--;
6430
6431     if (game.timegate_time_left == 0)
6432       CloseAllOpenTimegates();
6433   }
6434
6435   for (i=0; i<MAX_PLAYERS; i++)
6436   {
6437     struct PlayerInfo *player = &stored_player[i];
6438
6439     if (SHIELD_ON(player))
6440     {
6441       if (player->shield_deadly_time_left)
6442         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6443       else if (player->shield_normal_time_left)
6444         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6445     }
6446   }
6447
6448   if (TimeFrames >= FRAMES_PER_SECOND)
6449   {
6450     TimeFrames = 0;
6451     TimePlayed++;
6452
6453     for (i=0; i<MAX_PLAYERS; i++)
6454     {
6455       struct PlayerInfo *player = &stored_player[i];
6456
6457       if (SHIELD_ON(player))
6458       {
6459         player->shield_normal_time_left--;
6460
6461         if (player->shield_deadly_time_left > 0)
6462           player->shield_deadly_time_left--;
6463       }
6464     }
6465
6466     if (tape.recording || tape.playing)
6467       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6468
6469     if (TimeLeft > 0)
6470     {
6471       TimeLeft--;
6472
6473       if (TimeLeft <= 10 && setup.time_limit)
6474         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6475
6476       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6477
6478       if (!TimeLeft && setup.time_limit)
6479         for (i=0; i<MAX_PLAYERS; i++)
6480           KillHero(&stored_player[i]);
6481     }
6482     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6483       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6484   }
6485
6486   DrawAllPlayers();
6487
6488   if (options.debug)                    /* calculate frames per second */
6489   {
6490     static unsigned long fps_counter = 0;
6491     static int fps_frames = 0;
6492     unsigned long fps_delay_ms = Counter() - fps_counter;
6493
6494     fps_frames++;
6495
6496     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
6497     {
6498       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6499
6500       fps_frames = 0;
6501       fps_counter = Counter();
6502     }
6503
6504     redraw_mask |= REDRAW_FPS;
6505   }
6506
6507 #if 0
6508   if (stored_player[0].jx != stored_player[0].last_jx ||
6509       stored_player[0].jy != stored_player[0].last_jy)
6510     printf("::: %d, %d, %d, %d, %d\n",
6511            stored_player[0].MovDir,
6512            stored_player[0].MovPos,
6513            stored_player[0].GfxPos,
6514            stored_player[0].Frame,
6515            stored_player[0].StepFrame);
6516 #endif
6517
6518 #if 1
6519   FrameCounter++;
6520   TimeFrames++;
6521
6522   for (i=0; i<MAX_PLAYERS; i++)
6523   {
6524     int move_frames =
6525       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
6526
6527     stored_player[i].Frame += move_frames;
6528
6529     if (stored_player[i].MovPos != 0)
6530       stored_player[i].StepFrame += move_frames;
6531   }
6532 #endif
6533
6534 #if 1
6535   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
6536   {
6537     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
6538
6539     local_player->show_envelope = 0;
6540   }
6541 #endif
6542 }
6543
6544 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6545 {
6546   int min_x = x, min_y = y, max_x = x, max_y = y;
6547   int i;
6548
6549   for (i=0; i<MAX_PLAYERS; i++)
6550   {
6551     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6552
6553     if (!stored_player[i].active || &stored_player[i] == player)
6554       continue;
6555
6556     min_x = MIN(min_x, jx);
6557     min_y = MIN(min_y, jy);
6558     max_x = MAX(max_x, jx);
6559     max_y = MAX(max_y, jy);
6560   }
6561
6562   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6563 }
6564
6565 static boolean AllPlayersInVisibleScreen()
6566 {
6567   int i;
6568
6569   for (i=0; i<MAX_PLAYERS; i++)
6570   {
6571     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6572
6573     if (!stored_player[i].active)
6574       continue;
6575
6576     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6577       return FALSE;
6578   }
6579
6580   return TRUE;
6581 }
6582
6583 void ScrollLevel(int dx, int dy)
6584 {
6585   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6586   int x, y;
6587
6588   BlitBitmap(drawto_field, drawto_field,
6589              FX + TILEX * (dx == -1) - softscroll_offset,
6590              FY + TILEY * (dy == -1) - softscroll_offset,
6591              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6592              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6593              FX + TILEX * (dx == 1) - softscroll_offset,
6594              FY + TILEY * (dy == 1) - softscroll_offset);
6595
6596   if (dx)
6597   {
6598     x = (dx == 1 ? BX1 : BX2);
6599     for (y=BY1; y <= BY2; y++)
6600       DrawScreenField(x, y);
6601   }
6602
6603   if (dy)
6604   {
6605     y = (dy == 1 ? BY1 : BY2);
6606     for (x=BX1; x <= BX2; x++)
6607       DrawScreenField(x, y);
6608   }
6609
6610   redraw_mask |= REDRAW_FIELD;
6611 }
6612
6613 static void CheckGravityMovement(struct PlayerInfo *player)
6614 {
6615   if (game.gravity && !player->programmed_action)
6616   {
6617     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6618     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6619     int move_dir =
6620       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6621        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6622        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6623     int jx = player->jx, jy = player->jy;
6624     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6625     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6626     int new_jx = jx + dx, new_jy = jy + dy;
6627     boolean field_under_player_is_free =
6628       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6629     boolean player_is_moving_to_valid_field =
6630       (IN_LEV_FIELD(new_jx, new_jy) &&
6631        (Feld[new_jx][new_jy] == EL_SP_BASE ||
6632         Feld[new_jx][new_jy] == EL_SAND));
6633     /* !!! extend EL_SAND to anything diggable !!! */
6634
6635     if (field_under_player_is_free &&
6636         !player_is_moving_to_valid_field &&
6637         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6638       player->programmed_action = MV_DOWN;
6639   }
6640 }
6641
6642 /*
6643   MovePlayerOneStep()
6644   -----------------------------------------------------------------------------
6645   dx, dy:               direction (non-diagonal) to try to move the player to
6646   real_dx, real_dy:     direction as read from input device (can be diagonal)
6647 */
6648
6649 boolean MovePlayerOneStep(struct PlayerInfo *player,
6650                           int dx, int dy, int real_dx, int real_dy)
6651 {
6652 #if 0
6653   static int change_sides[4][2] =
6654   {
6655     /* enter side        leave side */
6656     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
6657     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
6658     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
6659     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
6660   };
6661   int move_direction = (dx == -1 ? MV_LEFT :
6662                         dx == +1 ? MV_RIGHT :
6663                         dy == -1 ? MV_UP :
6664                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
6665   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6666   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6667 #endif
6668   int jx = player->jx, jy = player->jy;
6669   int new_jx = jx + dx, new_jy = jy + dy;
6670   int element;
6671   int can_move;
6672
6673   if (!player->active || (!dx && !dy))
6674     return MF_NO_ACTION;
6675
6676   player->MovDir = (dx < 0 ? MV_LEFT :
6677                     dx > 0 ? MV_RIGHT :
6678                     dy < 0 ? MV_UP :
6679                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6680
6681   if (!IN_LEV_FIELD(new_jx, new_jy))
6682     return MF_NO_ACTION;
6683
6684   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6685     return MF_NO_ACTION;
6686
6687 #if 0
6688   element = MovingOrBlocked2Element(new_jx, new_jy);
6689 #else
6690   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6691 #endif
6692
6693   if (DONT_RUN_INTO(element))
6694   {
6695     if (element == EL_ACID && dx == 0 && dy == 1)
6696     {
6697       SplashAcid(jx, jy);
6698       Feld[jx][jy] = EL_PLAYER_1;
6699       InitMovingField(jx, jy, MV_DOWN);
6700       Store[jx][jy] = EL_ACID;
6701       ContinueMoving(jx, jy);
6702       BuryHero(player);
6703     }
6704     else
6705       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6706
6707     return MF_MOVING;
6708   }
6709
6710   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6711   if (can_move != MF_MOVING)
6712     return can_move;
6713
6714   /* check if DigField() has caused relocation of the player */
6715   if (player->jx != jx || player->jy != jy)
6716     return MF_NO_ACTION;
6717
6718   StorePlayer[jx][jy] = 0;
6719   player->last_jx = jx;
6720   player->last_jy = jy;
6721   player->jx = new_jx;
6722   player->jy = new_jy;
6723   StorePlayer[new_jx][new_jy] = player->element_nr;
6724
6725   player->MovPos =
6726     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6727
6728   ScrollPlayer(player, SCROLL_INIT);
6729
6730 #if 0
6731   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6732   {
6733     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6734                                     CE_OTHER_GETS_LEFT);
6735     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
6736                            CE_LEFT_BY_PLAYER, -1);
6737   }
6738
6739   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
6740   {
6741     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
6742                                     enter_side, CE_OTHER_GETS_ENTERED);
6743     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
6744                            CE_ENTERED_BY_PLAYER, -1);
6745   }
6746 #endif
6747
6748   return MF_MOVING;
6749 }
6750
6751 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
6752 {
6753   int jx = player->jx, jy = player->jy;
6754   int old_jx = jx, old_jy = jy;
6755   int moved = MF_NO_ACTION;
6756
6757   if (!player->active || (!dx && !dy))
6758     return FALSE;
6759
6760 #if 0
6761   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6762       !tape.playing)
6763     return FALSE;
6764 #else
6765   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6766       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6767     return FALSE;
6768 #endif
6769
6770   /* remove the last programmed player action */
6771   player->programmed_action = 0;
6772
6773   if (player->MovPos)
6774   {
6775     /* should only happen if pre-1.2 tape recordings are played */
6776     /* this is only for backward compatibility */
6777
6778     int original_move_delay_value = player->move_delay_value;
6779
6780 #if DEBUG
6781     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6782            tape.counter);
6783 #endif
6784
6785     /* scroll remaining steps with finest movement resolution */
6786     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6787
6788     while (player->MovPos)
6789     {
6790       ScrollPlayer(player, SCROLL_GO_ON);
6791       ScrollScreen(NULL, SCROLL_GO_ON);
6792       FrameCounter++;
6793       DrawAllPlayers();
6794       BackToFront();
6795     }
6796
6797     player->move_delay_value = original_move_delay_value;
6798   }
6799
6800   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6801   {
6802     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
6803       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
6804   }
6805   else
6806   {
6807     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
6808       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
6809   }
6810
6811   jx = player->jx;
6812   jy = player->jy;
6813
6814   if (moved & MF_MOVING && !ScreenMovPos &&
6815       (player == local_player || !options.network))
6816   {
6817     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6818     int offset = (setup.scroll_delay ? 3 : 0);
6819
6820     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6821     {
6822       /* actual player has left the screen -- scroll in that direction */
6823       if (jx != old_jx)         /* player has moved horizontally */
6824         scroll_x += (jx - old_jx);
6825       else                      /* player has moved vertically */
6826         scroll_y += (jy - old_jy);
6827     }
6828     else
6829     {
6830       if (jx != old_jx)         /* player has moved horizontally */
6831       {
6832         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6833             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6834           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6835
6836         /* don't scroll over playfield boundaries */
6837         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6838           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6839
6840         /* don't scroll more than one field at a time */
6841         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6842
6843         /* don't scroll against the player's moving direction */
6844         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6845             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6846           scroll_x = old_scroll_x;
6847       }
6848       else                      /* player has moved vertically */
6849       {
6850         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6851             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6852           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6853
6854         /* don't scroll over playfield boundaries */
6855         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6856           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6857
6858         /* don't scroll more than one field at a time */
6859         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6860
6861         /* don't scroll against the player's moving direction */
6862         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6863             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6864           scroll_y = old_scroll_y;
6865       }
6866     }
6867
6868     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6869     {
6870       if (!options.network && !AllPlayersInVisibleScreen())
6871       {
6872         scroll_x = old_scroll_x;
6873         scroll_y = old_scroll_y;
6874       }
6875       else
6876       {
6877         ScrollScreen(player, SCROLL_INIT);
6878         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6879       }
6880     }
6881   }
6882
6883 #if 0
6884 #if 1
6885   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6886 #else
6887   if (!(moved & MF_MOVING) && !player->is_pushing)
6888     player->Frame = 0;
6889 #endif
6890 #endif
6891
6892   player->StepFrame = 0;
6893
6894   if (moved & MF_MOVING)
6895   {
6896     if (old_jx != jx && old_jy == jy)
6897       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6898     else if (old_jx == jx && old_jy != jy)
6899       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6900
6901     DrawLevelField(jx, jy);     /* for "crumbled sand" */
6902
6903     player->last_move_dir = player->MovDir;
6904     player->is_moving = TRUE;
6905 #if 1
6906     player->is_snapping = FALSE;
6907 #endif
6908
6909 #if 1
6910     player->is_switching = FALSE;
6911 #endif
6912
6913
6914 #if 1
6915     {
6916       static int change_sides[4][2] =
6917       {
6918         /* enter side           leave side */
6919         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
6920         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
6921         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
6922         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
6923       };
6924       int move_direction = player->MovDir;
6925       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
6926       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
6927
6928 #if 1
6929       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
6930       {
6931         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6932                                         leave_side, CE_OTHER_GETS_LEFT);
6933         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
6934                                leave_side, CE_LEFT_BY_PLAYER, -1);
6935       }
6936
6937       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
6938       {
6939         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
6940                                         enter_side, CE_OTHER_GETS_ENTERED);
6941         CheckElementSideChange(jx, jy, Feld[jx][jy],
6942                                enter_side, CE_ENTERED_BY_PLAYER, -1);
6943       }
6944 #endif
6945
6946     }
6947 #endif
6948
6949
6950   }
6951   else
6952   {
6953     CheckGravityMovement(player);
6954
6955     /*
6956     player->last_move_dir = MV_NO_MOVING;
6957     */
6958     player->is_moving = FALSE;
6959   }
6960
6961   if (game.engine_version < VERSION_IDENT(3,0,7,0))
6962   {
6963     TestIfHeroTouchesBadThing(jx, jy);
6964     TestIfPlayerTouchesCustomElement(jx, jy);
6965   }
6966
6967   if (!player->active)
6968     RemoveHero(player);
6969
6970   return moved;
6971 }
6972
6973 void ScrollPlayer(struct PlayerInfo *player, int mode)
6974 {
6975   int jx = player->jx, jy = player->jy;
6976   int last_jx = player->last_jx, last_jy = player->last_jy;
6977   int move_stepsize = TILEX / player->move_delay_value;
6978
6979   if (!player->active || !player->MovPos)
6980     return;
6981
6982   if (mode == SCROLL_INIT)
6983   {
6984     player->actual_frame_counter = FrameCounter;
6985     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6986
6987     if (Feld[last_jx][last_jy] == EL_EMPTY)
6988       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6989
6990 #if 0
6991     DrawPlayer(player);
6992 #endif
6993     return;
6994   }
6995   else if (!FrameReached(&player->actual_frame_counter, 1))
6996     return;
6997
6998   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6999   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
7000
7001   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
7002     Feld[last_jx][last_jy] = EL_EMPTY;
7003
7004   /* before DrawPlayer() to draw correct player graphic for this case */
7005   if (player->MovPos == 0)
7006     CheckGravityMovement(player);
7007
7008 #if 0
7009   DrawPlayer(player);   /* needed here only to cleanup last field */
7010 #endif
7011
7012   if (player->MovPos == 0)      /* player reached destination field */
7013   {
7014     if (IS_PASSABLE(Feld[last_jx][last_jy]))
7015     {
7016       /* continue with normal speed after quickly moving through gate */
7017       HALVE_PLAYER_SPEED(player);
7018
7019       /* be able to make the next move without delay */
7020       player->move_delay = 0;
7021     }
7022
7023     player->last_jx = jx;
7024     player->last_jy = jy;
7025
7026     if (Feld[jx][jy] == EL_EXIT_OPEN ||
7027         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
7028         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
7029     {
7030       DrawPlayer(player);       /* needed here only to cleanup last field */
7031       RemoveHero(player);
7032
7033       if (local_player->friends_still_needed == 0 ||
7034           IS_SP_ELEMENT(Feld[jx][jy]))
7035         player->LevelSolved = player->GameOver = TRUE;
7036     }
7037
7038     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7039     {
7040       TestIfHeroTouchesBadThing(jx, jy);
7041       TestIfPlayerTouchesCustomElement(jx, jy);
7042 #if 1
7043       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
7044 #endif
7045
7046       if (!player->active)
7047         RemoveHero(player);
7048     }
7049
7050     if (tape.single_step && tape.recording && !tape.pausing &&
7051         !player->programmed_action)
7052       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7053   }
7054 }
7055
7056 void ScrollScreen(struct PlayerInfo *player, int mode)
7057 {
7058   static unsigned long screen_frame_counter = 0;
7059
7060   if (mode == SCROLL_INIT)
7061   {
7062     /* set scrolling step size according to actual player's moving speed */
7063     ScrollStepSize = TILEX / player->move_delay_value;
7064
7065     screen_frame_counter = FrameCounter;
7066     ScreenMovDir = player->MovDir;
7067     ScreenMovPos = player->MovPos;
7068     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7069     return;
7070   }
7071   else if (!FrameReached(&screen_frame_counter, 1))
7072     return;
7073
7074   if (ScreenMovPos)
7075   {
7076     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
7077     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
7078     redraw_mask |= REDRAW_FIELD;
7079   }
7080   else
7081     ScreenMovDir = MV_NO_MOVING;
7082 }
7083
7084 void TestIfPlayerTouchesCustomElement(int x, int y)
7085 {
7086   static int xy[4][2] =
7087   {
7088     { 0, -1 },
7089     { -1, 0 },
7090     { +1, 0 },
7091     { 0, +1 }
7092   };
7093   static int change_sides[4][2] =
7094   {
7095     /* center side       border side */
7096     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7097     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7098     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7099     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7100   };
7101   static int touch_dir[4] =
7102   {
7103     MV_LEFT | MV_RIGHT,
7104     MV_UP   | MV_DOWN,
7105     MV_UP   | MV_DOWN,
7106     MV_LEFT | MV_RIGHT
7107   };
7108   int center_element = Feld[x][y];      /* should always be non-moving! */
7109   int i;
7110
7111   for (i=0; i<4; i++)
7112   {
7113     int xx = x + xy[i][0];
7114     int yy = y + xy[i][1];
7115     int center_side = change_sides[i][0];
7116     int border_side = change_sides[i][1];
7117     int border_element;
7118
7119     if (!IN_LEV_FIELD(xx, yy))
7120       continue;
7121
7122     if (IS_PLAYER(x, y))
7123     {
7124       if (game.engine_version < VERSION_IDENT(3,0,7,0))
7125         border_element = Feld[xx][yy];          /* may be moving! */
7126       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7127         border_element = Feld[xx][yy];
7128       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
7129         border_element = MovingOrBlocked2Element(xx, yy);
7130       else
7131         continue;               /* center and border element do not touch */
7132
7133       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
7134                                       CE_OTHER_GETS_TOUCHED);
7135       CheckElementSideChange(xx, yy, border_element, border_side,
7136                              CE_TOUCHED_BY_PLAYER, -1);
7137     }
7138     else if (IS_PLAYER(xx, yy))
7139     {
7140       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7141       {
7142         struct PlayerInfo *player = PLAYERINFO(xx, yy);
7143
7144         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7145           continue;             /* center and border element do not touch */
7146       }
7147
7148       CheckTriggeredElementSideChange(x, y, center_element, center_side,
7149                                       CE_OTHER_GETS_TOUCHED);
7150       CheckElementSideChange(x, y, center_element, center_side,
7151                              CE_TOUCHED_BY_PLAYER, -1);
7152
7153       break;
7154     }
7155   }
7156 }
7157
7158 void TestIfElementTouchesCustomElement(int x, int y)
7159 {
7160   static int xy[4][2] =
7161   {
7162     { 0, -1 },
7163     { -1, 0 },
7164     { +1, 0 },
7165     { 0, +1 }
7166   };
7167   static int change_sides[4][2] =
7168   {
7169     /* center side       border side */
7170     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
7171     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
7172     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
7173     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
7174   };
7175   static int touch_dir[4] =
7176   {
7177     MV_LEFT | MV_RIGHT,
7178     MV_UP   | MV_DOWN,
7179     MV_UP   | MV_DOWN,
7180     MV_LEFT | MV_RIGHT
7181   };
7182   boolean change_center_element = FALSE;
7183   int center_element_change_page = 0;
7184   int center_element = Feld[x][y];      /* should always be non-moving! */
7185   int i, j;
7186
7187   for (i=0; i<4; i++)
7188   {
7189     int xx = x + xy[i][0];
7190     int yy = y + xy[i][1];
7191     int center_side = change_sides[i][0];
7192     int border_side = change_sides[i][1];
7193     int border_element;
7194
7195     if (!IN_LEV_FIELD(xx, yy))
7196       continue;
7197
7198     if (game.engine_version < VERSION_IDENT(3,0,7,0))
7199       border_element = Feld[xx][yy];    /* may be moving! */
7200     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
7201       border_element = Feld[xx][yy];
7202     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
7203       border_element = MovingOrBlocked2Element(xx, yy);
7204     else
7205       continue;                 /* center and border element do not touch */
7206
7207     /* check for change of center element (but change it only once) */
7208     if (IS_CUSTOM_ELEMENT(center_element) &&
7209         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
7210         !change_center_element)
7211     {
7212       for (j=0; j < element_info[center_element].num_change_pages; j++)
7213       {
7214         struct ElementChangeInfo *change =
7215           &element_info[center_element].change_page[j];
7216
7217         if (change->can_change &&
7218             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7219             change->sides & border_side &&
7220             change->trigger_element == border_element)
7221         {
7222           change_center_element = TRUE;
7223           center_element_change_page = j;
7224
7225           break;
7226         }
7227       }
7228     }
7229
7230     /* check for change of border element */
7231     if (IS_CUSTOM_ELEMENT(border_element) &&
7232         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
7233     {
7234       for (j=0; j < element_info[border_element].num_change_pages; j++)
7235       {
7236         struct ElementChangeInfo *change =
7237           &element_info[border_element].change_page[j];
7238
7239         if (change->can_change &&
7240             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
7241             change->sides & center_side &&
7242             change->trigger_element == center_element)
7243         {
7244           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
7245                                  CE_OTHER_IS_TOUCHING, j);
7246           break;
7247         }
7248       }
7249     }
7250   }
7251
7252   if (change_center_element)
7253     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
7254                            CE_OTHER_IS_TOUCHING, center_element_change_page);
7255 }
7256
7257 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
7258 {
7259   int i, kill_x = -1, kill_y = -1;
7260   static int test_xy[4][2] =
7261   {
7262     { 0, -1 },
7263     { -1, 0 },
7264     { +1, 0 },
7265     { 0, +1 }
7266   };
7267   static int test_dir[4] =
7268   {
7269     MV_UP,
7270     MV_LEFT,
7271     MV_RIGHT,
7272     MV_DOWN
7273   };
7274
7275   for (i=0; i<4; i++)
7276   {
7277     int test_x, test_y, test_move_dir, test_element;
7278
7279     test_x = good_x + test_xy[i][0];
7280     test_y = good_y + test_xy[i][1];
7281     if (!IN_LEV_FIELD(test_x, test_y))
7282       continue;
7283
7284     test_move_dir =
7285       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7286
7287 #if 0
7288     test_element = Feld[test_x][test_y];
7289 #else
7290     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
7291 #endif
7292
7293     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7294        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7295     */
7296     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
7297         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
7298     {
7299       kill_x = test_x;
7300       kill_y = test_y;
7301       break;
7302     }
7303   }
7304
7305   if (kill_x != -1 || kill_y != -1)
7306   {
7307     if (IS_PLAYER(good_x, good_y))
7308     {
7309       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
7310
7311       if (player->shield_deadly_time_left > 0)
7312         Bang(kill_x, kill_y);
7313       else if (!PLAYER_PROTECTED(good_x, good_y))
7314         KillHero(player);
7315     }
7316     else
7317       Bang(good_x, good_y);
7318   }
7319 }
7320
7321 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
7322 {
7323   int i, kill_x = -1, kill_y = -1;
7324   int bad_element = Feld[bad_x][bad_y];
7325   static int test_xy[4][2] =
7326   {
7327     { 0, -1 },
7328     { -1, 0 },
7329     { +1, 0 },
7330     { 0, +1 }
7331   };
7332   static int touch_dir[4] =
7333   {
7334     MV_LEFT | MV_RIGHT,
7335     MV_UP   | MV_DOWN,
7336     MV_UP   | MV_DOWN,
7337     MV_LEFT | MV_RIGHT
7338   };
7339   static int test_dir[4] =
7340   {
7341     MV_UP,
7342     MV_LEFT,
7343     MV_RIGHT,
7344     MV_DOWN
7345   };
7346
7347   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
7348     return;
7349
7350   for (i=0; i<4; i++)
7351   {
7352     int test_x, test_y, test_move_dir, test_element;
7353
7354     test_x = bad_x + test_xy[i][0];
7355     test_y = bad_y + test_xy[i][1];
7356     if (!IN_LEV_FIELD(test_x, test_y))
7357       continue;
7358
7359     test_move_dir =
7360       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
7361
7362     test_element = Feld[test_x][test_y];
7363
7364     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
7365        2nd case: DONT_TOUCH style bad thing does not move away from good thing
7366     */
7367     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
7368         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
7369     {
7370       /* good thing is player or penguin that does not move away */
7371       if (IS_PLAYER(test_x, test_y))
7372       {
7373         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
7374
7375         if (bad_element == EL_ROBOT && player->is_moving)
7376           continue;     /* robot does not kill player if he is moving */
7377
7378         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
7379         {
7380           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
7381             continue;           /* center and border element do not touch */
7382         }
7383
7384         kill_x = test_x;
7385         kill_y = test_y;
7386         break;
7387       }
7388       else if (test_element == EL_PENGUIN)
7389       {
7390         kill_x = test_x;
7391         kill_y = test_y;
7392         break;
7393       }
7394     }
7395   }
7396
7397   if (kill_x != -1 || kill_y != -1)
7398   {
7399     if (IS_PLAYER(kill_x, kill_y))
7400     {
7401       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
7402
7403       if (player->shield_deadly_time_left > 0)
7404         Bang(bad_x, bad_y);
7405       else if (!PLAYER_PROTECTED(kill_x, kill_y))
7406         KillHero(player);
7407     }
7408     else
7409       Bang(kill_x, kill_y);
7410   }
7411 }
7412
7413 void TestIfHeroTouchesBadThing(int x, int y)
7414 {
7415   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7416 }
7417
7418 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
7419 {
7420   TestIfGoodThingHitsBadThing(x, y, move_dir);
7421 }
7422
7423 void TestIfBadThingTouchesHero(int x, int y)
7424 {
7425   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7426 }
7427
7428 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
7429 {
7430   TestIfBadThingHitsGoodThing(x, y, move_dir);
7431 }
7432
7433 void TestIfFriendTouchesBadThing(int x, int y)
7434 {
7435   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
7436 }
7437
7438 void TestIfBadThingTouchesFriend(int x, int y)
7439 {
7440   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
7441 }
7442
7443 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
7444 {
7445   int i, kill_x = bad_x, kill_y = bad_y;
7446   static int xy[4][2] =
7447   {
7448     { 0, -1 },
7449     { -1, 0 },
7450     { +1, 0 },
7451     { 0, +1 }
7452   };
7453
7454   for (i=0; i<4; i++)
7455   {
7456     int x, y, element;
7457
7458     x = bad_x + xy[i][0];
7459     y = bad_y + xy[i][1];
7460     if (!IN_LEV_FIELD(x, y))
7461       continue;
7462
7463     element = Feld[x][y];
7464     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
7465         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
7466     {
7467       kill_x = x;
7468       kill_y = y;
7469       break;
7470     }
7471   }
7472
7473   if (kill_x != bad_x || kill_y != bad_y)
7474     Bang(bad_x, bad_y);
7475 }
7476
7477 void KillHero(struct PlayerInfo *player)
7478 {
7479   int jx = player->jx, jy = player->jy;
7480
7481   if (!player->active)
7482     return;
7483
7484   /* remove accessible field at the player's position */
7485   Feld[jx][jy] = EL_EMPTY;
7486
7487   /* deactivate shield (else Bang()/Explode() would not work right) */
7488   player->shield_normal_time_left = 0;
7489   player->shield_deadly_time_left = 0;
7490
7491   Bang(jx, jy);
7492   BuryHero(player);
7493 }
7494
7495 static void KillHeroUnlessProtected(int x, int y)
7496 {
7497   if (!PLAYER_PROTECTED(x, y))
7498     KillHero(PLAYERINFO(x, y));
7499 }
7500
7501 void BuryHero(struct PlayerInfo *player)
7502 {
7503   int jx = player->jx, jy = player->jy;
7504
7505   if (!player->active)
7506     return;
7507
7508 #if 1
7509   PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
7510 #else
7511   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
7512 #endif
7513   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
7514
7515   player->GameOver = TRUE;
7516   RemoveHero(player);
7517 }
7518
7519 void RemoveHero(struct PlayerInfo *player)
7520 {
7521   int jx = player->jx, jy = player->jy;
7522   int i, found = FALSE;
7523
7524   player->present = FALSE;
7525   player->active = FALSE;
7526
7527   if (!ExplodeField[jx][jy])
7528     StorePlayer[jx][jy] = 0;
7529
7530   for (i=0; i<MAX_PLAYERS; i++)
7531     if (stored_player[i].active)
7532       found = TRUE;
7533
7534   if (!found)
7535     AllPlayersGone = TRUE;
7536
7537   ExitX = ZX = jx;
7538   ExitY = ZY = jy;
7539 }
7540
7541 /*
7542   =============================================================================
7543   checkDiagonalPushing()
7544   -----------------------------------------------------------------------------
7545   check if diagonal input device direction results in pushing of object
7546   (by checking if the alternative direction is walkable, diggable, ...)
7547   =============================================================================
7548 */
7549
7550 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7551                                     int x, int y, int real_dx, int real_dy)
7552 {
7553   int jx, jy, dx, dy, xx, yy;
7554
7555   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
7556     return TRUE;
7557
7558   /* diagonal direction: check alternative direction */
7559   jx = player->jx;
7560   jy = player->jy;
7561   dx = x - jx;
7562   dy = y - jy;
7563   xx = jx + (dx == 0 ? real_dx : 0);
7564   yy = jy + (dy == 0 ? real_dy : 0);
7565
7566   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7567 }
7568
7569 /*
7570   =============================================================================
7571   DigField()
7572   -----------------------------------------------------------------------------
7573   x, y:                 field next to player (non-diagonal) to try to dig to
7574   real_dx, real_dy:     direction as read from input device (can be diagonal)
7575   =============================================================================
7576 */
7577
7578 int DigField(struct PlayerInfo *player,
7579              int x, int y, int real_dx, int real_dy, int mode)
7580 {
7581   static int change_sides[4] =
7582   {
7583     CH_SIDE_RIGHT,      /* moving left  */
7584     CH_SIDE_LEFT,       /* moving right */
7585     CH_SIDE_BOTTOM,     /* moving up    */
7586     CH_SIDE_TOP,        /* moving down  */
7587   };
7588   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
7589   int jx = player->jx, jy = player->jy;
7590   int dx = x - jx, dy = y - jy;
7591   int nextx = x + dx, nexty = y + dy;
7592   int move_direction = (dx == -1 ? MV_LEFT :
7593                         dx == +1 ? MV_RIGHT :
7594                         dy == -1 ? MV_UP :
7595                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7596   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
7597   int element;
7598
7599   if (player->MovPos == 0)
7600   {
7601     player->is_digging = FALSE;
7602     player->is_collecting = FALSE;
7603   }
7604
7605   if (player->MovPos == 0)      /* last pushing move finished */
7606     player->is_pushing = FALSE;
7607
7608   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
7609   {
7610     player->is_switching = FALSE;
7611     player->push_delay = 0;
7612
7613     return MF_NO_ACTION;
7614   }
7615
7616   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7617     return MF_NO_ACTION;
7618
7619 #if 0
7620   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7621 #else
7622   if (IS_TUBE(Feld[jx][jy]) ||
7623       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
7624 #endif
7625   {
7626     int i = 0;
7627     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7628     int tube_leave_directions[][2] =
7629     {
7630       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7631       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
7632       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
7633       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
7634       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
7635       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
7636       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
7637       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
7638       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
7639       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
7640       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
7641       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7642     };
7643
7644     while (tube_leave_directions[i][0] != tube_element)
7645     {
7646       i++;
7647       if (tube_leave_directions[i][0] == -1)    /* should not happen */
7648         break;
7649     }
7650
7651     if (!(tube_leave_directions[i][1] & move_direction))
7652       return MF_NO_ACTION;      /* tube has no opening in this direction */
7653   }
7654
7655   element = Feld[x][y];
7656
7657   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7658       game.engine_version >= VERSION_IDENT(2,2,0,0))
7659     return MF_NO_ACTION;
7660
7661   switch (element)
7662   {
7663     case EL_SP_PORT_LEFT:
7664     case EL_SP_PORT_RIGHT:
7665     case EL_SP_PORT_UP:
7666     case EL_SP_PORT_DOWN:
7667     case EL_SP_PORT_HORIZONTAL:
7668     case EL_SP_PORT_VERTICAL:
7669     case EL_SP_PORT_ANY:
7670     case EL_SP_GRAVITY_PORT_LEFT:
7671     case EL_SP_GRAVITY_PORT_RIGHT:
7672     case EL_SP_GRAVITY_PORT_UP:
7673     case EL_SP_GRAVITY_PORT_DOWN:
7674       if ((dx == -1 &&
7675            element != EL_SP_PORT_LEFT &&
7676            element != EL_SP_GRAVITY_PORT_LEFT &&
7677            element != EL_SP_PORT_HORIZONTAL &&
7678            element != EL_SP_PORT_ANY) ||
7679           (dx == +1 &&
7680            element != EL_SP_PORT_RIGHT &&
7681            element != EL_SP_GRAVITY_PORT_RIGHT &&
7682            element != EL_SP_PORT_HORIZONTAL &&
7683            element != EL_SP_PORT_ANY) ||
7684           (dy == -1 &&
7685            element != EL_SP_PORT_UP &&
7686            element != EL_SP_GRAVITY_PORT_UP &&
7687            element != EL_SP_PORT_VERTICAL &&
7688            element != EL_SP_PORT_ANY) ||
7689           (dy == +1 &&
7690            element != EL_SP_PORT_DOWN &&
7691            element != EL_SP_GRAVITY_PORT_DOWN &&
7692            element != EL_SP_PORT_VERTICAL &&
7693            element != EL_SP_PORT_ANY) ||
7694           !IN_LEV_FIELD(nextx, nexty) ||
7695           !IS_FREE(nextx, nexty))
7696         return MF_NO_ACTION;
7697
7698       if (element == EL_SP_GRAVITY_PORT_LEFT ||
7699           element == EL_SP_GRAVITY_PORT_RIGHT ||
7700           element == EL_SP_GRAVITY_PORT_UP ||
7701           element == EL_SP_GRAVITY_PORT_DOWN)
7702         game.gravity = !game.gravity;
7703
7704       /* automatically move to the next field with double speed */
7705       player->programmed_action = move_direction;
7706       DOUBLE_PLAYER_SPEED(player);
7707
7708       PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7709       break;
7710
7711     case EL_TUBE_ANY:
7712     case EL_TUBE_VERTICAL:
7713     case EL_TUBE_HORIZONTAL:
7714     case EL_TUBE_VERTICAL_LEFT:
7715     case EL_TUBE_VERTICAL_RIGHT:
7716     case EL_TUBE_HORIZONTAL_UP:
7717     case EL_TUBE_HORIZONTAL_DOWN:
7718     case EL_TUBE_LEFT_UP:
7719     case EL_TUBE_LEFT_DOWN:
7720     case EL_TUBE_RIGHT_UP:
7721     case EL_TUBE_RIGHT_DOWN:
7722       {
7723         int i = 0;
7724         int tube_enter_directions[][2] =
7725         {
7726           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7727           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
7728           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
7729           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
7730           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
7731           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
7732           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
7733           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
7734           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
7735           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
7736           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
7737           { -1,                         MV_NO_MOVING                         }
7738         };
7739
7740         while (tube_enter_directions[i][0] != element)
7741         {
7742           i++;
7743           if (tube_enter_directions[i][0] == -1)        /* should not happen */
7744             break;
7745         }
7746
7747         if (!(tube_enter_directions[i][1] & move_direction))
7748           return MF_NO_ACTION;  /* tube has no opening in this direction */
7749
7750         PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7751       }
7752       break;
7753
7754     default:
7755
7756       if (IS_WALKABLE(element))
7757       {
7758         int sound_action = ACTION_WALKING;
7759
7760         if (element >= EL_GATE_1 && element <= EL_GATE_4)
7761         {
7762           if (!player->key[element - EL_GATE_1])
7763             return MF_NO_ACTION;
7764         }
7765         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7766         {
7767           if (!player->key[element - EL_GATE_1_GRAY])
7768             return MF_NO_ACTION;
7769         }
7770         else if (element == EL_EXIT_OPEN ||
7771                  element == EL_SP_EXIT_OPEN ||
7772                  element == EL_SP_EXIT_OPENING)
7773         {
7774           sound_action = ACTION_PASSING;        /* player is passing exit */
7775         }
7776         else if (element == EL_EMPTY)
7777         {
7778           sound_action = ACTION_MOVING;         /* nothing to walk on */
7779         }
7780
7781         /* play sound from background or player, whatever is available */
7782         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7783           PlaySoundLevelElementAction(x, y, element, sound_action);
7784         else
7785           PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7786
7787         break;
7788       }
7789       else if (IS_PASSABLE(element))
7790       {
7791         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7792           return MF_NO_ACTION;
7793
7794 #if 1
7795         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
7796           return MF_NO_ACTION;
7797 #endif
7798
7799         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7800         {
7801           if (!player->key[element - EL_EM_GATE_1])
7802             return MF_NO_ACTION;
7803         }
7804         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7805         {
7806           if (!player->key[element - EL_EM_GATE_1_GRAY])
7807             return MF_NO_ACTION;
7808         }
7809
7810         /* automatically move to the next field with double speed */
7811         player->programmed_action = move_direction;
7812         DOUBLE_PLAYER_SPEED(player);
7813
7814         PlaySoundLevelAction(x, y, ACTION_PASSING);
7815
7816         break;
7817       }
7818       else if (IS_DIGGABLE(element))
7819       {
7820         RemoveField(x, y);
7821
7822         if (mode != DF_SNAP)
7823         {
7824 #if 1
7825           GfxElement[x][y] = GFX_ELEMENT(element);
7826 #else
7827           GfxElement[x][y] =
7828             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7829 #endif
7830           player->is_digging = TRUE;
7831         }
7832
7833         PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7834
7835         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
7836
7837 #if 1
7838         if (mode == DF_SNAP)
7839           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7840 #endif
7841
7842         break;
7843       }
7844       else if (IS_COLLECTIBLE(element))
7845       {
7846         RemoveField(x, y);
7847
7848         if (mode != DF_SNAP)
7849         {
7850           GfxElement[x][y] = element;
7851           player->is_collecting = TRUE;
7852         }
7853
7854         if (element == EL_SPEED_PILL)
7855           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7856         else if (element == EL_EXTRA_TIME && level.time > 0)
7857         {
7858           TimeLeft += 10;
7859           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7860         }
7861         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7862         {
7863           player->shield_normal_time_left += 10;
7864           if (element == EL_SHIELD_DEADLY)
7865             player->shield_deadly_time_left += 10;
7866         }
7867         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7868         {
7869           if (player->inventory_size < MAX_INVENTORY_SIZE)
7870             player->inventory_element[player->inventory_size++] = element;
7871
7872           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7873                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7874         }
7875         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7876         {
7877           player->dynabomb_count++;
7878           player->dynabombs_left++;
7879         }
7880         else if (element == EL_DYNABOMB_INCREASE_SIZE)
7881         {
7882           player->dynabomb_size++;
7883         }
7884         else if (element == EL_DYNABOMB_INCREASE_POWER)
7885         {
7886           player->dynabomb_xl = TRUE;
7887         }
7888         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7889                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7890         {
7891           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7892                         element - EL_KEY_1 : element - EL_EM_KEY_1);
7893
7894           player->key[key_nr] = TRUE;
7895
7896           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7897                              el2edimg(EL_KEY_1 + key_nr));
7898           redraw_mask |= REDRAW_DOOR_1;
7899         }
7900         else if (IS_ENVELOPE(element))
7901         {
7902 #if 1
7903           player->show_envelope = element;
7904 #else
7905           ShowEnvelope(element - EL_ENVELOPE_1);
7906 #endif
7907         }
7908         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7909         {
7910           int i;
7911
7912           for (i=0; i < element_info[element].collect_count; i++)
7913             if (player->inventory_size < MAX_INVENTORY_SIZE)
7914               player->inventory_element[player->inventory_size++] = element;
7915
7916           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7917                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7918         }
7919         else if (element_info[element].collect_count > 0)
7920         {
7921           local_player->gems_still_needed -=
7922             element_info[element].collect_count;
7923           if (local_player->gems_still_needed < 0)
7924             local_player->gems_still_needed = 0;
7925
7926           DrawText(DX_EMERALDS, DY_EMERALDS,
7927                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7928         }
7929
7930         RaiseScoreElement(element);
7931         PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7932
7933         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7934
7935 #if 1
7936         if (mode == DF_SNAP)
7937           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
7938 #endif
7939
7940         break;
7941       }
7942       else if (IS_PUSHABLE(element))
7943       {
7944         if (mode == DF_SNAP && element != EL_BD_ROCK)
7945           return MF_NO_ACTION;
7946
7947         if (CAN_FALL(element) && dy)
7948           return MF_NO_ACTION;
7949
7950         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7951             !(element == EL_SPRING && use_spring_bug))
7952           return MF_NO_ACTION;
7953
7954 #if 1
7955         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
7956             ((move_direction & MV_VERTICAL &&
7957               ((element_info[element].move_pattern & MV_LEFT &&
7958                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
7959                (element_info[element].move_pattern & MV_RIGHT &&
7960                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
7961              (move_direction & MV_HORIZONTAL &&
7962               ((element_info[element].move_pattern & MV_UP &&
7963                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
7964                (element_info[element].move_pattern & MV_DOWN &&
7965                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
7966           return MF_NO_ACTION;
7967 #endif
7968
7969 #if 1
7970         /* do not push elements already moving away faster than player */
7971         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
7972             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
7973           return MF_NO_ACTION;
7974 #else
7975         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7976           return MF_NO_ACTION;
7977 #endif
7978
7979 #if 1
7980         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
7981         {
7982           if (player->push_delay_value == -1)
7983             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7984         }
7985         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
7986         {
7987           if (!player->is_pushing)
7988             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7989         }
7990
7991         /*
7992         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
7993             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
7994              !player_is_pushing))
7995           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7996         */
7997 #else
7998         if (!player->is_pushing &&
7999             game.engine_version >= VERSION_IDENT(2,2,0,7))
8000           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8001 #endif
8002
8003 #if 0
8004         printf("::: push delay: %ld [%d, %d] [%d]\n",
8005                player->push_delay_value, FrameCounter, game.engine_version,
8006                player->is_pushing);
8007 #endif
8008
8009         player->is_pushing = TRUE;
8010
8011         if (!(IN_LEV_FIELD(nextx, nexty) &&
8012               (IS_FREE(nextx, nexty) ||
8013                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
8014                 IS_SB_ELEMENT(element)))))
8015           return MF_NO_ACTION;
8016
8017         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
8018           return MF_NO_ACTION;
8019
8020         if (player->push_delay == 0)    /* new pushing; restart delay */
8021           player->push_delay = FrameCounter;
8022
8023         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
8024             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
8025             element != EL_SPRING && element != EL_BALLOON)
8026         {
8027           /* make sure that there is no move delay before next try to push */
8028           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
8029             player->move_delay = INITIAL_MOVE_DELAY_OFF;
8030
8031           return MF_NO_ACTION;
8032         }
8033
8034 #if 0
8035         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
8036 #endif
8037
8038         if (IS_SB_ELEMENT(element))
8039         {
8040           if (element == EL_SOKOBAN_FIELD_FULL)
8041           {
8042             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
8043             local_player->sokobanfields_still_needed++;
8044           }
8045
8046           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
8047           {
8048             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
8049             local_player->sokobanfields_still_needed--;
8050           }
8051
8052           Feld[x][y] = EL_SOKOBAN_OBJECT;
8053
8054           if (Back[x][y] == Back[nextx][nexty])
8055             PlaySoundLevelAction(x, y, ACTION_PUSHING);
8056           else if (Back[x][y] != 0)
8057             PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
8058                                         ACTION_EMPTYING);
8059           else
8060             PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
8061                                         ACTION_FILLING);
8062
8063           if (local_player->sokobanfields_still_needed == 0 &&
8064               game.emulation == EMU_SOKOBAN)
8065           {
8066             player->LevelSolved = player->GameOver = TRUE;
8067             PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
8068           }
8069         }
8070         else
8071           PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
8072
8073         InitMovingField(x, y, move_direction);
8074         GfxAction[x][y] = ACTION_PUSHING;
8075
8076         if (mode == DF_SNAP)
8077           ContinueMoving(x, y);
8078         else
8079           MovPos[x][y] = (dx != 0 ? dx : dy);
8080
8081         Pushed[x][y] = TRUE;
8082         Pushed[nextx][nexty] = TRUE;
8083
8084         if (game.engine_version < VERSION_IDENT(2,2,0,7))
8085           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
8086         else
8087           player->push_delay_value = -1;        /* get new value later */
8088
8089         CheckTriggeredElementSideChange(x, y, element, dig_side,
8090                                         CE_OTHER_GETS_PUSHED);
8091         CheckElementSideChange(x, y, element, dig_side,
8092                                CE_PUSHED_BY_PLAYER, -1);
8093
8094         break;
8095       }
8096       else if (IS_SWITCHABLE(element))
8097       {
8098         if (PLAYER_SWITCHING(player, x, y))
8099           return MF_ACTION;
8100
8101         player->is_switching = TRUE;
8102         player->switch_x = x;
8103         player->switch_y = y;
8104
8105         PlaySoundLevelElementAction(x, y, element, ACTION_ACTIVATING);
8106
8107         if (element == EL_ROBOT_WHEEL)
8108         {
8109           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
8110           ZX = x;
8111           ZY = y;
8112
8113           DrawLevelField(x, y);
8114         }
8115         else if (element == EL_SP_TERMINAL)
8116         {
8117           int xx, yy;
8118
8119           for (yy=0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
8120           {
8121             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
8122               Bang(xx, yy);
8123             else if (Feld[xx][yy] == EL_SP_TERMINAL)
8124               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
8125           }
8126         }
8127         else if (IS_BELT_SWITCH(element))
8128         {
8129           ToggleBeltSwitch(x, y);
8130         }
8131         else if (element == EL_SWITCHGATE_SWITCH_UP ||
8132                  element == EL_SWITCHGATE_SWITCH_DOWN)
8133         {
8134           ToggleSwitchgateSwitch(x, y);
8135         }
8136         else if (element == EL_LIGHT_SWITCH ||
8137                  element == EL_LIGHT_SWITCH_ACTIVE)
8138         {
8139           ToggleLightSwitch(x, y);
8140
8141 #if 0
8142           PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
8143                          SND_LIGHT_SWITCH_ACTIVATING :
8144                          SND_LIGHT_SWITCH_DEACTIVATING);
8145 #endif
8146         }
8147         else if (element == EL_TIMEGATE_SWITCH)
8148         {
8149           ActivateTimegateSwitch(x, y);
8150         }
8151         else if (element == EL_BALLOON_SWITCH_LEFT ||
8152                  element == EL_BALLOON_SWITCH_RIGHT ||
8153                  element == EL_BALLOON_SWITCH_UP ||
8154                  element == EL_BALLOON_SWITCH_DOWN ||
8155                  element == EL_BALLOON_SWITCH_ANY)
8156         {
8157           if (element == EL_BALLOON_SWITCH_ANY)
8158             game.balloon_dir = move_direction;
8159           else
8160             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
8161                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
8162                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
8163                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
8164                                 MV_NO_MOVING);
8165         }
8166         else if (element == EL_LAMP)
8167         {
8168           Feld[x][y] = EL_LAMP_ACTIVE;
8169           local_player->lights_still_needed--;
8170
8171           DrawLevelField(x, y);
8172         }
8173         else if (element == EL_TIME_ORB_FULL)
8174         {
8175           Feld[x][y] = EL_TIME_ORB_EMPTY;
8176           TimeLeft += 10;
8177           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
8178
8179           DrawLevelField(x, y);
8180
8181 #if 0
8182           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
8183 #endif
8184         }
8185
8186         return MF_ACTION;
8187       }
8188       else
8189       {
8190         if (!PLAYER_SWITCHING(player, x, y))
8191         {
8192           player->is_switching = TRUE;
8193           player->switch_x = x;
8194           player->switch_y = y;
8195
8196           CheckTriggeredElementSideChange(x, y, element, dig_side,
8197                                           CE_OTHER_IS_SWITCHING);
8198           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
8199         }
8200
8201         CheckTriggeredElementSideChange(x, y, element, dig_side,
8202                                         CE_OTHER_GETS_PRESSED);
8203         CheckElementSideChange(x, y, element, dig_side,
8204                                CE_PRESSED_BY_PLAYER, -1);
8205       }
8206
8207       return MF_NO_ACTION;
8208   }
8209
8210   player->push_delay = 0;
8211
8212   if (Feld[x][y] != element)            /* really digged/collected something */
8213     player->is_collecting = !player->is_digging;
8214
8215   return MF_MOVING;
8216 }
8217
8218 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
8219 {
8220   int jx = player->jx, jy = player->jy;
8221   int x = jx + dx, y = jy + dy;
8222   int snap_direction = (dx == -1 ? MV_LEFT :
8223                         dx == +1 ? MV_RIGHT :
8224                         dy == -1 ? MV_UP :
8225                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8226
8227   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
8228     return FALSE;
8229
8230   if (!player->active || !IN_LEV_FIELD(x, y))
8231     return FALSE;
8232
8233   if (dx && dy)
8234     return FALSE;
8235
8236   if (!dx && !dy)
8237   {
8238     if (player->MovPos == 0)
8239       player->is_pushing = FALSE;
8240
8241     player->is_snapping = FALSE;
8242
8243     if (player->MovPos == 0)
8244     {
8245       player->is_moving = FALSE;
8246       player->is_digging = FALSE;
8247       player->is_collecting = FALSE;
8248     }
8249
8250     return FALSE;
8251   }
8252
8253   if (player->is_snapping)
8254     return FALSE;
8255
8256   player->MovDir = snap_direction;
8257
8258   player->is_moving = FALSE;
8259   player->is_digging = FALSE;
8260   player->is_collecting = FALSE;
8261
8262   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
8263     return FALSE;
8264
8265   player->is_snapping = TRUE;
8266
8267   player->is_moving = FALSE;
8268   player->is_digging = FALSE;
8269   player->is_collecting = FALSE;
8270
8271   DrawLevelField(x, y);
8272   BackToFront();
8273
8274   return TRUE;
8275 }
8276
8277 boolean DropElement(struct PlayerInfo *player)
8278 {
8279   int jx = player->jx, jy = player->jy;
8280   int old_element;
8281
8282   if (!player->active || player->MovPos)
8283     return FALSE;
8284
8285   old_element = Feld[jx][jy];
8286
8287   /* check if player has anything that can be dropped */
8288   if (player->inventory_size == 0 && player->dynabombs_left == 0)
8289     return FALSE;
8290
8291   /* check if anything can be dropped at the current position */
8292   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
8293     return FALSE;
8294
8295   /* collected custom elements can only be dropped on empty fields */
8296   if (player->inventory_size > 0 &&
8297       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
8298       && old_element != EL_EMPTY)
8299     return FALSE;
8300
8301   if (old_element != EL_EMPTY)
8302     Back[jx][jy] = old_element;         /* store old element on this field */
8303
8304   MovDelay[jx][jy] = 96;
8305
8306   ResetGfxAnimation(jx, jy);
8307   ResetRandomAnimationValue(jx, jy);
8308
8309   if (player->inventory_size > 0)
8310   {
8311     int new_element = player->inventory_element[--player->inventory_size];
8312
8313     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
8314                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
8315                     new_element);
8316
8317     DrawText(DX_DYNAMITE, DY_DYNAMITE,
8318              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
8319
8320     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8321       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8322
8323     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8324
8325     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
8326     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
8327
8328     TestIfElementTouchesCustomElement(jx, jy);
8329   }
8330   else          /* player is dropping a dyna bomb */
8331   {
8332     player->dynabombs_left--;
8333
8334     Feld[jx][jy] =
8335       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
8336
8337     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
8338       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
8339
8340     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
8341   }
8342
8343   return TRUE;
8344 }
8345
8346 /* ------------------------------------------------------------------------- */
8347 /* game sound playing functions                                              */
8348 /* ------------------------------------------------------------------------- */
8349
8350 static int *loop_sound_frame = NULL;
8351 static int *loop_sound_volume = NULL;
8352
8353 void InitPlaySoundLevel()
8354 {
8355   int num_sounds = getSoundListSize();
8356
8357   if (loop_sound_frame != NULL)
8358     free(loop_sound_frame);
8359
8360   if (loop_sound_volume != NULL)
8361     free(loop_sound_volume);
8362
8363   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
8364   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
8365 }
8366
8367 static void PlaySoundLevel(int x, int y, int nr)
8368 {
8369   int sx = SCREENX(x), sy = SCREENY(y);
8370   int volume, stereo_position;
8371   int max_distance = 8;
8372   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
8373
8374   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
8375       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
8376     return;
8377
8378   if (!IN_LEV_FIELD(x, y) ||
8379       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
8380       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
8381     return;
8382
8383   volume = SOUND_MAX_VOLUME;
8384
8385   if (!IN_SCR_FIELD(sx, sy))
8386   {
8387     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
8388     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
8389
8390     volume -= volume * (dx > dy ? dx : dy) / max_distance;
8391   }
8392
8393   stereo_position = (SOUND_MAX_LEFT +
8394                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
8395                      (SCR_FIELDX + 2 * max_distance));
8396
8397   if (IS_LOOP_SOUND(nr))
8398   {
8399     /* This assures that quieter loop sounds do not overwrite louder ones,
8400        while restarting sound volume comparison with each new game frame. */
8401
8402     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
8403       return;
8404
8405     loop_sound_volume[nr] = volume;
8406     loop_sound_frame[nr] = FrameCounter;
8407   }
8408
8409   PlaySoundExt(nr, volume, stereo_position, type);
8410 }
8411
8412 static void PlaySoundLevelNearest(int x, int y, int sound_action)
8413 {
8414   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
8415                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
8416                  y < LEVELY(BY1) ? LEVELY(BY1) :
8417                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
8418                  sound_action);
8419 }
8420
8421 static void PlaySoundLevelAction(int x, int y, int action)
8422 {
8423   PlaySoundLevelElementAction(x, y, Feld[x][y], action);
8424 }
8425
8426 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
8427 {
8428   int sound_effect = element_info[element].sound[action];
8429
8430   if (sound_effect != SND_UNDEFINED)
8431     PlaySoundLevel(x, y, sound_effect);
8432 }
8433
8434 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
8435 {
8436   int sound_effect = element_info[Feld[x][y]].sound[action];
8437
8438   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8439     PlaySoundLevel(x, y, sound_effect);
8440 }
8441
8442 static void StopSoundLevelActionIfLoop(int x, int y, int action)
8443 {
8444   int sound_effect = element_info[Feld[x][y]].sound[action];
8445
8446   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
8447     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
8448 }
8449
8450 void RaiseScore(int value)
8451 {
8452   local_player->score += value;
8453   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
8454 }
8455
8456 void RaiseScoreElement(int element)
8457 {
8458   switch(element)
8459   {
8460     case EL_EMERALD:
8461     case EL_BD_DIAMOND:
8462     case EL_EMERALD_YELLOW:
8463     case EL_EMERALD_RED:
8464     case EL_EMERALD_PURPLE:
8465     case EL_SP_INFOTRON:
8466       RaiseScore(level.score[SC_EMERALD]);
8467       break;
8468     case EL_DIAMOND:
8469       RaiseScore(level.score[SC_DIAMOND]);
8470       break;
8471     case EL_CRYSTAL:
8472       RaiseScore(level.score[SC_CRYSTAL]);
8473       break;
8474     case EL_PEARL:
8475       RaiseScore(level.score[SC_PEARL]);
8476       break;
8477     case EL_BUG:
8478     case EL_BD_BUTTERFLY:
8479     case EL_SP_ELECTRON:
8480       RaiseScore(level.score[SC_BUG]);
8481       break;
8482     case EL_SPACESHIP:
8483     case EL_BD_FIREFLY:
8484     case EL_SP_SNIKSNAK:
8485       RaiseScore(level.score[SC_SPACESHIP]);
8486       break;
8487     case EL_YAMYAM:
8488     case EL_DARK_YAMYAM:
8489       RaiseScore(level.score[SC_YAMYAM]);
8490       break;
8491     case EL_ROBOT:
8492       RaiseScore(level.score[SC_ROBOT]);
8493       break;
8494     case EL_PACMAN:
8495       RaiseScore(level.score[SC_PACMAN]);
8496       break;
8497     case EL_NUT:
8498       RaiseScore(level.score[SC_NUT]);
8499       break;
8500     case EL_DYNAMITE:
8501     case EL_SP_DISK_RED:
8502     case EL_DYNABOMB_INCREASE_NUMBER:
8503     case EL_DYNABOMB_INCREASE_SIZE:
8504     case EL_DYNABOMB_INCREASE_POWER:
8505       RaiseScore(level.score[SC_DYNAMITE]);
8506       break;
8507     case EL_SHIELD_NORMAL:
8508     case EL_SHIELD_DEADLY:
8509       RaiseScore(level.score[SC_SHIELD]);
8510       break;
8511     case EL_EXTRA_TIME:
8512       RaiseScore(level.score[SC_TIME_BONUS]);
8513       break;
8514     case EL_KEY_1:
8515     case EL_KEY_2:
8516     case EL_KEY_3:
8517     case EL_KEY_4:
8518       RaiseScore(level.score[SC_KEY]);
8519       break;
8520     default:
8521       RaiseScore(element_info[element].collect_score);
8522       break;
8523   }
8524 }
8525
8526 void RequestQuitGame(boolean ask_if_really_quit)
8527 {
8528   if (AllPlayersGone ||
8529       !ask_if_really_quit ||
8530       level_editor_test_game ||
8531       Request("Do you really want to quit the game ?",
8532               REQ_ASK | REQ_STAY_CLOSED))
8533   {
8534 #if defined(PLATFORM_UNIX)
8535     if (options.network)
8536       SendToServer_StopPlaying();
8537     else
8538 #endif
8539     {
8540       game_status = GAME_MODE_MAIN;
8541       DrawMainMenu();
8542     }
8543   }
8544   else
8545   {
8546     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
8547   }
8548 }
8549
8550
8551 /* ---------- new game button stuff ---------------------------------------- */
8552
8553 /* graphic position values for game buttons */
8554 #define GAME_BUTTON_XSIZE       30
8555 #define GAME_BUTTON_YSIZE       30
8556 #define GAME_BUTTON_XPOS        5
8557 #define GAME_BUTTON_YPOS        215
8558 #define SOUND_BUTTON_XPOS       5
8559 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
8560
8561 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8562 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8563 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8564 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
8565 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
8566 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
8567
8568 static struct
8569 {
8570   int x, y;
8571   int gadget_id;
8572   char *infotext;
8573 } gamebutton_info[NUM_GAME_BUTTONS] =
8574 {
8575   {
8576     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
8577     GAME_CTRL_ID_STOP,
8578     "stop game"
8579   },
8580   {
8581     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
8582     GAME_CTRL_ID_PAUSE,
8583     "pause game"
8584   },
8585   {
8586     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
8587     GAME_CTRL_ID_PLAY,
8588     "play game"
8589   },
8590   {
8591     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
8592     SOUND_CTRL_ID_MUSIC,
8593     "background music on/off"
8594   },
8595   {
8596     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
8597     SOUND_CTRL_ID_LOOPS,
8598     "sound loops on/off"
8599   },
8600   {
8601     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
8602     SOUND_CTRL_ID_SIMPLE,
8603     "normal sounds on/off"
8604   }
8605 };
8606
8607 void CreateGameButtons()
8608 {
8609   int i;
8610
8611   for (i=0; i<NUM_GAME_BUTTONS; i++)
8612   {
8613     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8614     struct GadgetInfo *gi;
8615     int button_type;
8616     boolean checked;
8617     unsigned long event_mask;
8618     int gd_xoffset, gd_yoffset;
8619     int gd_x1, gd_x2, gd_y1, gd_y2;
8620     int id = i;
8621
8622     gd_xoffset = gamebutton_info[i].x;
8623     gd_yoffset = gamebutton_info[i].y;
8624     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8625     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8626
8627     if (id == GAME_CTRL_ID_STOP ||
8628         id == GAME_CTRL_ID_PAUSE ||
8629         id == GAME_CTRL_ID_PLAY)
8630     {
8631       button_type = GD_TYPE_NORMAL_BUTTON;
8632       checked = FALSE;
8633       event_mask = GD_EVENT_RELEASED;
8634       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8635       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8636     }
8637     else
8638     {
8639       button_type = GD_TYPE_CHECK_BUTTON;
8640       checked =
8641         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8642          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8643          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8644       event_mask = GD_EVENT_PRESSED;
8645       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
8646       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8647     }
8648
8649     gi = CreateGadget(GDI_CUSTOM_ID, id,
8650                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
8651                       GDI_X, DX + gd_xoffset,
8652                       GDI_Y, DY + gd_yoffset,
8653                       GDI_WIDTH, GAME_BUTTON_XSIZE,
8654                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
8655                       GDI_TYPE, button_type,
8656                       GDI_STATE, GD_BUTTON_UNPRESSED,
8657                       GDI_CHECKED, checked,
8658                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8659                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8660                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8661                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8662                       GDI_EVENT_MASK, event_mask,
8663                       GDI_CALLBACK_ACTION, HandleGameButtons,
8664                       GDI_END);
8665
8666     if (gi == NULL)
8667       Error(ERR_EXIT, "cannot create gadget");
8668
8669     game_gadget[id] = gi;
8670   }
8671 }
8672
8673 void FreeGameButtons()
8674 {
8675   int i;
8676
8677   for (i=0; i<NUM_GAME_BUTTONS; i++)
8678     FreeGadget(game_gadget[i]);
8679 }
8680
8681 static void MapGameButtons()
8682 {
8683   int i;
8684
8685   for (i=0; i<NUM_GAME_BUTTONS; i++)
8686     MapGadget(game_gadget[i]);
8687 }
8688
8689 void UnmapGameButtons()
8690 {
8691   int i;
8692
8693   for (i=0; i<NUM_GAME_BUTTONS; i++)
8694     UnmapGadget(game_gadget[i]);
8695 }
8696
8697 static void HandleGameButtons(struct GadgetInfo *gi)
8698 {
8699   int id = gi->custom_id;
8700
8701   if (game_status != GAME_MODE_PLAYING)
8702     return;
8703
8704   switch (id)
8705   {
8706     case GAME_CTRL_ID_STOP:
8707       RequestQuitGame(TRUE);
8708       break;
8709
8710     case GAME_CTRL_ID_PAUSE:
8711       if (options.network)
8712       {
8713 #if defined(PLATFORM_UNIX)
8714         if (tape.pausing)
8715           SendToServer_ContinuePlaying();
8716         else
8717           SendToServer_PausePlaying();
8718 #endif
8719       }
8720       else
8721         TapeTogglePause(TAPE_TOGGLE_MANUAL);
8722       break;
8723
8724     case GAME_CTRL_ID_PLAY:
8725       if (tape.pausing)
8726       {
8727 #if defined(PLATFORM_UNIX)
8728         if (options.network)
8729           SendToServer_ContinuePlaying();
8730         else
8731 #endif
8732         {
8733           tape.pausing = FALSE;
8734           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8735         }
8736       }
8737       break;
8738
8739     case SOUND_CTRL_ID_MUSIC:
8740       if (setup.sound_music)
8741       { 
8742         setup.sound_music = FALSE;
8743         FadeMusic();
8744       }
8745       else if (audio.music_available)
8746       { 
8747         setup.sound = setup.sound_music = TRUE;
8748
8749         SetAudioMode(setup.sound);
8750         PlayMusic(level_nr);
8751       }
8752       break;
8753
8754     case SOUND_CTRL_ID_LOOPS:
8755       if (setup.sound_loops)
8756         setup.sound_loops = FALSE;
8757       else if (audio.loops_available)
8758       {
8759         setup.sound = setup.sound_loops = TRUE;
8760         SetAudioMode(setup.sound);
8761       }
8762       break;
8763
8764     case SOUND_CTRL_ID_SIMPLE:
8765       if (setup.sound_simple)
8766         setup.sound_simple = FALSE;
8767       else if (audio.sound_available)
8768       {
8769         setup.sound = setup.sound_simple = TRUE;
8770         SetAudioMode(setup.sound);
8771       }
8772       break;
8773
8774     default:
8775       break;
8776   }
8777 }