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