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