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