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