rnd-20030823-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MoveFigure() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollFigure() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME                 29
63 #define YY_TIME                 194
64
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL                (DX + XX_LEVEL)
67 #define DY_LEVEL                (DY + YY_LEVEL)
68 #define DX_EMERALDS             (DX + XX_EMERALDS)
69 #define DY_EMERALDS             (DY + YY_EMERALDS)
70 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
72 #define DX_KEYS                 (DX + XX_KEYS)
73 #define DY_KEYS                 (DY + YY_KEYS)
74 #define DX_SCORE                (DX + XX_SCORE)
75 #define DY_SCORE                (DY + YY_SCORE)
76 #define DX_TIME                 (DX + XX_TIME)
77 #define DY_TIME                 (DY + YY_TIME)
78
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF  -1
81 #define INITIAL_MOVE_DELAY_ON   0
82
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED   4
86
87 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
91
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
94
95 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
96
97 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
98                                  RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
100                                  RND(element_info[e].move_delay_random))
101
102 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
103                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
104                                         (condition) ||                  \
105                                         (DONT_COLLIDE_WITH(e) &&        \
106                                          IS_FREE_OR_PLAYER(x, y))))
107
108 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
109                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
110                                         (condition)))
111
112 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
113         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
114
115 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
116         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
117
118 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
119         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
120
121 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
122
123 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
124                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
125                                         Feld[x][y] == EL_DIAMOND))
126
127 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
128                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
129                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
130
131 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
132                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
133                                         IS_AMOEBOID(Feld[x][y])))
134
135 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
136                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
137                                         IS_FOOD_PIG(Feld[x][y])))
138
139 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
140                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
141                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
142                                         Feld[x][y] == EL_EXIT_OPEN ||   \
143                                         Feld[x][y] == EL_ACID))
144
145 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
146                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
147
148 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
149 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
150
151 /* game button identifiers */
152 #define GAME_CTRL_ID_STOP               0
153 #define GAME_CTRL_ID_PAUSE              1
154 #define GAME_CTRL_ID_PLAY               2
155 #define SOUND_CTRL_ID_MUSIC             3
156 #define SOUND_CTRL_ID_LOOPS             4
157 #define SOUND_CTRL_ID_SIMPLE            5
158
159 #define NUM_GAME_BUTTONS                6
160
161
162 /* forward declaration for internal use */
163
164 static void InitBeltMovement(void);
165 static void CloseAllOpenTimegates(void);
166 static void CheckGravityMovement(struct PlayerInfo *);
167 static void KillHeroUnlessProtected(int, int);
168
169 static void TestIfPlayerTouchesCustomElement(int, int);
170 static void TestIfElementTouchesCustomElement(int, int);
171
172 static boolean 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   /* prevent CheckTriggeredElementChange() from looping */
5352   Changing[x][y] = TRUE;
5353
5354   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
5355
5356   Changing[x][y] = FALSE;
5357
5358   if (change->explode)
5359   {
5360     Bang(x, y);
5361     return;
5362   }
5363
5364   if (change->use_content)
5365   {
5366     boolean complete_change = TRUE;
5367     boolean can_change[3][3];
5368     int xx, yy;
5369
5370     for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5371     {
5372       boolean half_destructible;
5373       int ex = x + xx - 1;
5374       int ey = y + yy - 1;
5375       int e;
5376
5377       can_change[xx][yy] = TRUE;
5378
5379       if (ex == x && ey == y)   /* do not check changing element itself */
5380         continue;
5381
5382       if (change->content[xx][yy] == EL_EMPTY_SPACE)
5383       {
5384         can_change[xx][yy] = FALSE;     /* do not change empty borders */
5385
5386         continue;
5387       }
5388
5389       if (!IN_LEV_FIELD(ex, ey))
5390       {
5391         can_change[xx][yy] = FALSE;
5392         complete_change = FALSE;
5393
5394         continue;
5395       }
5396
5397       e = Feld[ex][ey];
5398
5399       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5400         e = MovingOrBlocked2Element(ex, ey);
5401
5402       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
5403
5404       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
5405           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
5406           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
5407       {
5408         can_change[xx][yy] = FALSE;
5409         complete_change = FALSE;
5410       }
5411     }
5412
5413     if (!change->only_complete || complete_change)
5414     {
5415       boolean something_has_changed = FALSE;
5416
5417       if (change->only_complete && change->use_random_change &&
5418           RND(100) < change->random)
5419         return;
5420
5421       for (yy = 0; yy < 3; yy++) for(xx = 0; xx < 3 ; xx++)
5422       {
5423         int ex = x + xx - 1;
5424         int ey = y + yy - 1;
5425
5426         if (can_change[xx][yy] && (!change->use_random_change ||
5427                                    RND(100) < change->random))
5428         {
5429           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5430             RemoveMovingField(ex, ey);
5431
5432           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
5433
5434           something_has_changed = TRUE;
5435
5436           /* for symmetry reasons, stop newly created border elements */
5437           if (ex != x || ey != y)
5438             Stop[ex][ey] = TRUE;
5439         }
5440       }
5441
5442       if (something_has_changed)
5443         PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5444     }
5445   }
5446   else
5447   {
5448     ChangeElementNowExt(x, y, change->target_element);
5449
5450     PlaySoundLevelElementAction(x, y, element, ACTION_CHANGING);
5451   }
5452 }
5453
5454 static void ChangeElement(int x, int y)
5455 {
5456 #if 1
5457   int element = MovingOrBlocked2Element(x, y);
5458 #else
5459   int element = Feld[x][y];
5460 #endif
5461   struct ElementChangeInfo *change = &element_info[element].change;
5462
5463   if (ChangeDelay[x][y] == 0)           /* initialize element change */
5464   {
5465 #if 1
5466     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
5467                          RND(change->delay_random * change->delay_frames)) + 1;
5468 #else
5469     ChangeDelay[x][y] = changing_element[element].change_delay + 1;
5470
5471     if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
5472     {
5473       int max_random_delay = element_info[element].change.delay_random;
5474       int delay_frames = element_info[element].change.delay_frames;
5475
5476       ChangeDelay[x][y] += RND(max_random_delay * delay_frames);
5477     }
5478 #endif
5479
5480     ResetGfxAnimation(x, y);
5481     ResetRandomAnimationValue(x, y);
5482
5483 #if 1
5484     if (change->pre_change_function)
5485       change->pre_change_function(x, y);
5486 #else
5487     if (changing_element[element].pre_change_function)
5488       changing_element[element].pre_change_function(x, y);
5489 #endif
5490   }
5491
5492   ChangeDelay[x][y]--;
5493
5494   if (ChangeDelay[x][y] != 0)           /* continue element change */
5495   {
5496     int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5497
5498     if (IS_ANIMATED(graphic))
5499       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5500
5501 #if 1
5502     if (change->change_function)
5503       change->change_function(x, y);
5504 #else
5505     if (changing_element[element].change_function)
5506       changing_element[element].change_function(x, y);
5507 #endif
5508   }
5509   else                                  /* finish element change */
5510   {
5511 #if 0
5512     int next_element = changing_element[element].next_element;
5513 #endif
5514
5515     if (IS_MOVING(x, y))                /* never change a running system ;-) */
5516     {
5517       ChangeDelay[x][y] = 1;            /* try change after next move step */
5518
5519       return;
5520     }
5521
5522 #if 1
5523     ChangeElementNow(x, y, element);
5524
5525     if (change->post_change_function)
5526       change->post_change_function(x, y);
5527 #else
5528     if (next_element != EL_UNDEFINED)
5529       ChangeElementNow(x, y, next_element);
5530     else
5531       ChangeElementNow(x, y, element_info[element].change.target_element);
5532
5533     if (changing_element[element].post_change_function)
5534       changing_element[element].post_change_function(x, y);
5535 #endif
5536   }
5537 }
5538
5539 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
5540                                            int trigger_event)
5541 {
5542   int i, x, y;
5543
5544   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
5545     return FALSE;
5546
5547   for (i=0; i<MAX_NUM_ELEMENTS; i++)
5548   {
5549     if (!CAN_CHANGE(i) || !HAS_CHANGE_EVENT(i, trigger_event) ||
5550         element_info[i].change.trigger_element != trigger_element)
5551       continue;
5552
5553     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5554     {
5555       if (x == lx && y == ly)   /* do not change trigger element itself */
5556         continue;
5557
5558       if (Changing[x][y])       /* do not change just changing elements */
5559         continue;
5560
5561       if (Feld[x][y] == i)
5562       {
5563         ChangeDelay[x][y] = 1;
5564         ChangeElement(x, y);
5565       }
5566     }
5567   }
5568
5569   return TRUE;
5570 }
5571
5572 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
5573 {
5574   if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, trigger_event))
5575     return FALSE;
5576
5577   if (Feld[x][y] == EL_BLOCKED)
5578     Blocked2Moving(x, y, &x, &y);
5579
5580   ChangeDelay[x][y] = 1;
5581   ChangeElement(x, y);
5582
5583   return TRUE;
5584 }
5585
5586 static void PlayerActions(struct PlayerInfo *player, byte player_action)
5587 {
5588   static byte stored_player_action[MAX_PLAYERS];
5589   static int num_stored_actions = 0;
5590   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
5591   int left      = player_action & JOY_LEFT;
5592   int right     = player_action & JOY_RIGHT;
5593   int up        = player_action & JOY_UP;
5594   int down      = player_action & JOY_DOWN;
5595   int button1   = player_action & JOY_BUTTON_1;
5596   int button2   = player_action & JOY_BUTTON_2;
5597   int dx        = (left ? -1    : right ? 1     : 0);
5598   int dy        = (up   ? -1    : down  ? 1     : 0);
5599
5600   stored_player_action[player->index_nr] = 0;
5601   num_stored_actions++;
5602
5603   if (!player->active || tape.pausing)
5604     return;
5605
5606   if (player_action)
5607   {
5608     if (button1)
5609       snapped = SnapField(player, dx, dy);
5610     else
5611     {
5612       if (button2)
5613         dropped = DropElement(player);
5614
5615       moved = MoveFigure(player, dx, dy);
5616     }
5617
5618     if (tape.single_step && tape.recording && !tape.pausing)
5619     {
5620       if (button1 || (dropped && !moved))
5621       {
5622         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
5623         SnapField(player, 0, 0);                /* stop snapping */
5624       }
5625     }
5626
5627     stored_player_action[player->index_nr] = player_action;
5628   }
5629   else
5630   {
5631     /* no actions for this player (no input at player's configured device) */
5632
5633     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
5634     SnapField(player, 0, 0);
5635     CheckGravityMovement(player);
5636
5637     if (player->MovPos == 0)
5638     {
5639 #if 0
5640       printf("Trying... Player frame reset\n");
5641 #endif
5642
5643       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
5644     }
5645
5646     if (player->MovPos == 0)    /* needed for tape.playing */
5647       player->is_moving = FALSE;
5648   }
5649
5650   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
5651   {
5652     TapeRecordAction(stored_player_action);
5653     num_stored_actions = 0;
5654   }
5655 }
5656
5657 void GameActions()
5658 {
5659   static unsigned long action_delay = 0;
5660   unsigned long action_delay_value;
5661   int magic_wall_x = 0, magic_wall_y = 0;
5662   int i, x, y, element, graphic;
5663   byte *recorded_player_action;
5664   byte summarized_player_action = 0;
5665
5666   if (game_status != GAME_MODE_PLAYING)
5667     return;
5668
5669   action_delay_value =
5670     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
5671
5672   if (tape.playing && tape.index_search && !tape.pausing)
5673     action_delay_value = 0;
5674
5675   /* ---------- main game synchronization point ---------- */
5676
5677   WaitUntilDelayReached(&action_delay, action_delay_value);
5678
5679   if (network_playing && !network_player_action_received)
5680   {
5681     /*
5682 #ifdef DEBUG
5683     printf("DEBUG: try to get network player actions in time\n");
5684 #endif
5685     */
5686
5687 #if defined(PLATFORM_UNIX)
5688     /* last chance to get network player actions without main loop delay */
5689     HandleNetworking();
5690 #endif
5691
5692     if (game_status != GAME_MODE_PLAYING)
5693       return;
5694
5695     if (!network_player_action_received)
5696     {
5697       /*
5698 #ifdef DEBUG
5699       printf("DEBUG: failed to get network player actions in time\n");
5700 #endif
5701       */
5702       return;
5703     }
5704   }
5705
5706   if (tape.pausing)
5707     return;
5708
5709   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
5710
5711   for (i=0; i<MAX_PLAYERS; i++)
5712   {
5713     summarized_player_action |= stored_player[i].action;
5714
5715     if (!network_playing)
5716       stored_player[i].effective_action = stored_player[i].action;
5717   }
5718
5719 #if defined(PLATFORM_UNIX)
5720   if (network_playing)
5721     SendToServer_MovePlayer(summarized_player_action);
5722 #endif
5723
5724   if (!options.network && !setup.team_mode)
5725     local_player->effective_action = summarized_player_action;
5726
5727   for (i=0; i<MAX_PLAYERS; i++)
5728   {
5729     int actual_player_action = stored_player[i].effective_action;
5730
5731     if (stored_player[i].programmed_action)
5732       actual_player_action = stored_player[i].programmed_action;
5733
5734     if (recorded_player_action)
5735       actual_player_action = recorded_player_action[i];
5736
5737     PlayerActions(&stored_player[i], actual_player_action);
5738     ScrollFigure(&stored_player[i], SCROLL_GO_ON);
5739   }
5740
5741   network_player_action_received = FALSE;
5742
5743   ScrollScreen(NULL, SCROLL_GO_ON);
5744
5745 #if 0
5746   FrameCounter++;
5747   TimeFrames++;
5748
5749   for (i=0; i<MAX_PLAYERS; i++)
5750     stored_player[i].Frame++;
5751 #endif
5752
5753 #if 1
5754   if (game.engine_version < RELEASE_IDENT(2,2,0,7))
5755   {
5756     for (i=0; i<MAX_PLAYERS; i++)
5757     {
5758       struct PlayerInfo *player = &stored_player[i];
5759       int x = player->jx;
5760       int y = player->jy;
5761
5762       if (player->active && player->Pushing && player->is_moving &&
5763           IS_MOVING(x, y))
5764       {
5765         ContinueMoving(x, y);
5766
5767         /* continue moving after pushing (this is actually a bug) */
5768         if (!IS_MOVING(x, y))
5769         {
5770           Stop[x][y] = FALSE;
5771         }
5772       }
5773     }
5774   }
5775 #endif
5776
5777   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5778   {
5779     Stop[x][y] = FALSE;
5780     if (JustStopped[x][y] > 0)
5781       JustStopped[x][y]--;
5782
5783     GfxFrame[x][y]++;
5784
5785 #if 1
5786     /* reset finished pushing action (not done in ContinueMoving() to allow
5787        continous pushing animation for elements without push delay) */
5788     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
5789     {
5790       ResetGfxAnimation(x, y);
5791       DrawLevelField(x, y);
5792     }
5793 #endif
5794
5795 #if DEBUG
5796     if (IS_BLOCKED(x, y))
5797     {
5798       int oldx, oldy;
5799
5800       Blocked2Moving(x, y, &oldx, &oldy);
5801       if (!IS_MOVING(oldx, oldy))
5802       {
5803         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
5804         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
5805         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
5806         printf("GameActions(): This should never happen!\n");
5807       }
5808     }
5809 #endif
5810   }
5811
5812   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
5813   {
5814     element = Feld[x][y];
5815 #if 1
5816     graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5817 #else
5818     graphic = el2img(element);
5819 #endif
5820
5821 #if 0
5822     if (element == -1)
5823     {
5824       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
5825
5826       element = graphic = 0;
5827     }
5828 #endif
5829
5830     if (graphic_info[graphic].anim_global_sync)
5831       GfxFrame[x][y] = FrameCounter;
5832
5833     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
5834         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
5835       ResetRandomAnimationValue(x, y);
5836
5837     SetRandomAnimationValue(x, y);
5838
5839 #if 1
5840     PlaySoundLevelActionIfLoop(x, y, GfxAction[x][y]);
5841 #endif
5842
5843     if (IS_INACTIVE(element))
5844     {
5845       if (IS_ANIMATED(graphic))
5846         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5847
5848       continue;
5849     }
5850
5851 #if 1
5852     /* this may take place after moving, so 'element' may have changed */
5853     if (IS_CHANGING(x, y))
5854     {
5855       ChangeElement(x, y);
5856       element = Feld[x][y];
5857       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5858     }
5859 #endif
5860
5861     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
5862     {
5863       StartMoving(x, y);
5864
5865 #if 1
5866       graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
5867 #if 0
5868       if (element == EL_MOLE)
5869         printf("::: %d, %d, %d [%d]\n",
5870                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
5871                GfxAction[x][y]);
5872 #endif
5873 #if 0
5874       if (element == EL_YAMYAM)
5875         printf("::: %d, %d, %d\n",
5876                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
5877 #endif
5878 #endif
5879
5880       if (IS_ANIMATED(graphic) &&
5881           !IS_MOVING(x, y) &&
5882           !Stop[x][y])
5883       {
5884         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5885
5886 #if 0
5887         if (element == EL_MOLE)
5888           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
5889 #endif
5890       }
5891
5892       if (IS_GEM(element) || element == EL_SP_INFOTRON)
5893         EdelsteinFunkeln(x, y);
5894     }
5895     else if ((element == EL_ACID ||
5896               element == EL_EXIT_OPEN ||
5897               element == EL_SP_EXIT_OPEN ||
5898               element == EL_SP_TERMINAL ||
5899               element == EL_SP_TERMINAL_ACTIVE ||
5900               element == EL_EXTRA_TIME ||
5901               element == EL_SHIELD_NORMAL ||
5902               element == EL_SHIELD_DEADLY) &&
5903              IS_ANIMATED(graphic))
5904       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5905     else if (IS_MOVING(x, y))
5906       ContinueMoving(x, y);
5907     else if (IS_ACTIVE_BOMB(element))
5908       CheckDynamite(x, y);
5909 #if 0
5910     else if (element == EL_EXPLOSION && !game.explosions_delayed)
5911       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
5912 #endif
5913     else if (element == EL_AMOEBA_GROWING)
5914       AmoebeWaechst(x, y);
5915     else if (element == EL_AMOEBA_SHRINKING)
5916       AmoebaDisappearing(x, y);
5917
5918 #if !USE_NEW_AMOEBA_CODE
5919     else if (IS_AMOEBALIVE(element))
5920       AmoebeAbleger(x, y);
5921 #endif
5922
5923     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
5924       Life(x, y);
5925     else if (element == EL_EXIT_CLOSED)
5926       CheckExit(x, y);
5927     else if (element == EL_SP_EXIT_CLOSED)
5928       CheckExitSP(x, y);
5929     else if (element == EL_EXPANDABLE_WALL_GROWING)
5930       MauerWaechst(x, y);
5931     else if (element == EL_EXPANDABLE_WALL ||
5932              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
5933              element == EL_EXPANDABLE_WALL_VERTICAL ||
5934              element == EL_EXPANDABLE_WALL_ANY)
5935       MauerAbleger(x, y);
5936     else if (element == EL_FLAMES)
5937       CheckForDragon(x, y);
5938 #if 0
5939     else if (IS_AUTO_CHANGING(element))
5940       ChangeElement(x, y);
5941 #endif
5942     else if (element == EL_EXPLOSION)
5943       ; /* drawing of correct explosion animation is handled separately */
5944     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
5945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5946
5947 #if 0
5948     /* this may take place after moving, so 'element' may have changed */
5949     if (IS_AUTO_CHANGING(Feld[x][y]))
5950       ChangeElement(x, y);
5951 #endif
5952
5953     if (IS_BELT_ACTIVE(element))
5954       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
5955
5956     if (game.magic_wall_active)
5957     {
5958       int jx = local_player->jx, jy = local_player->jy;
5959
5960       /* play the element sound at the position nearest to the player */
5961       if ((element == EL_MAGIC_WALL_FULL ||
5962            element == EL_MAGIC_WALL_ACTIVE ||
5963            element == EL_MAGIC_WALL_EMPTYING ||
5964            element == EL_BD_MAGIC_WALL_FULL ||
5965            element == EL_BD_MAGIC_WALL_ACTIVE ||
5966            element == EL_BD_MAGIC_WALL_EMPTYING) &&
5967           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
5968       {
5969         magic_wall_x = x;
5970         magic_wall_y = y;
5971       }
5972     }
5973   }
5974
5975 #if USE_NEW_AMOEBA_CODE
5976   /* new experimental amoeba growth stuff */
5977 #if 1
5978   if (!(FrameCounter % 8))
5979 #endif
5980   {
5981     static unsigned long random = 1684108901;
5982
5983     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
5984     {
5985 #if 0
5986       x = (random >> 10) % lev_fieldx;
5987       y = (random >> 20) % lev_fieldy;
5988 #else
5989       x = RND(lev_fieldx);
5990       y = RND(lev_fieldy);
5991 #endif
5992       element = Feld[x][y];
5993
5994       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5995       if (!IS_PLAYER(x,y) &&
5996           (element == EL_EMPTY ||
5997            element == EL_SAND ||
5998            element == EL_QUICKSAND_EMPTY ||
5999            element == EL_ACID_SPLASH_LEFT ||
6000            element == EL_ACID_SPLASH_RIGHT))
6001       {
6002         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
6003             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
6004             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
6005             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
6006           Feld[x][y] = EL_AMOEBA_DROP;
6007       }
6008
6009       random = random * 129 + 1;
6010     }
6011   }
6012 #endif
6013
6014 #if 0
6015   if (game.explosions_delayed)
6016 #endif
6017   {
6018     game.explosions_delayed = FALSE;
6019
6020     for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6021     {
6022       element = Feld[x][y];
6023
6024       if (ExplodeField[x][y])
6025         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
6026       else if (element == EL_EXPLOSION)
6027         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
6028
6029       ExplodeField[x][y] = EX_NO_EXPLOSION;
6030     }
6031
6032     game.explosions_delayed = TRUE;
6033   }
6034
6035   if (game.magic_wall_active)
6036   {
6037     if (!(game.magic_wall_time_left % 4))
6038     {
6039       int element = Feld[magic_wall_x][magic_wall_y];
6040
6041       if (element == EL_BD_MAGIC_WALL_FULL ||
6042           element == EL_BD_MAGIC_WALL_ACTIVE ||
6043           element == EL_BD_MAGIC_WALL_EMPTYING)
6044         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
6045       else
6046         PlaySoundLevel(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
6047     }
6048
6049     if (game.magic_wall_time_left > 0)
6050     {
6051       game.magic_wall_time_left--;
6052       if (!game.magic_wall_time_left)
6053       {
6054         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
6055         {
6056           element = Feld[x][y];
6057
6058           if (element == EL_MAGIC_WALL_ACTIVE ||
6059               element == EL_MAGIC_WALL_FULL)
6060           {
6061             Feld[x][y] = EL_MAGIC_WALL_DEAD;
6062             DrawLevelField(x, y);
6063           }
6064           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
6065                    element == EL_BD_MAGIC_WALL_FULL)
6066           {
6067             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6068             DrawLevelField(x, y);
6069           }
6070         }
6071
6072         game.magic_wall_active = FALSE;
6073       }
6074     }
6075   }
6076
6077   if (game.light_time_left > 0)
6078   {
6079     game.light_time_left--;
6080
6081     if (game.light_time_left == 0)
6082       RedrawAllLightSwitchesAndInvisibleElements();
6083   }
6084
6085   if (game.timegate_time_left > 0)
6086   {
6087     game.timegate_time_left--;
6088
6089     if (game.timegate_time_left == 0)
6090       CloseAllOpenTimegates();
6091   }
6092
6093   for (i=0; i<MAX_PLAYERS; i++)
6094   {
6095     struct PlayerInfo *player = &stored_player[i];
6096
6097     if (SHIELD_ON(player))
6098     {
6099       if (player->shield_deadly_time_left)
6100         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
6101       else if (player->shield_normal_time_left)
6102         PlaySoundLevel(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
6103     }
6104   }
6105
6106   if (TimeFrames >= (1000 / GameFrameDelay))
6107   {
6108     TimeFrames = 0;
6109     TimePlayed++;
6110
6111     for (i=0; i<MAX_PLAYERS; i++)
6112     {
6113       struct PlayerInfo *player = &stored_player[i];
6114
6115       if (SHIELD_ON(player))
6116       {
6117         player->shield_normal_time_left--;
6118
6119         if (player->shield_deadly_time_left > 0)
6120           player->shield_deadly_time_left--;
6121       }
6122     }
6123
6124     if (tape.recording || tape.playing)
6125       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
6126
6127     if (TimeLeft > 0)
6128     {
6129       TimeLeft--;
6130
6131       if (TimeLeft <= 10 && setup.time_limit)
6132         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
6133
6134       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
6135
6136       if (!TimeLeft && setup.time_limit)
6137         for (i=0; i<MAX_PLAYERS; i++)
6138           KillHero(&stored_player[i]);
6139     }
6140     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
6141       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
6142   }
6143
6144   DrawAllPlayers();
6145
6146   if (options.debug)                    /* calculate frames per second */
6147   {
6148     static unsigned long fps_counter = 0;
6149     static int fps_frames = 0;
6150     unsigned long fps_delay_ms = Counter() - fps_counter;
6151
6152     fps_frames++;
6153
6154     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
6155     {
6156       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
6157
6158       fps_frames = 0;
6159       fps_counter = Counter();
6160     }
6161
6162     redraw_mask |= REDRAW_FPS;
6163   }
6164
6165 #if 0
6166   if (stored_player[0].jx != stored_player[0].last_jx ||
6167       stored_player[0].jy != stored_player[0].last_jy)
6168     printf("::: %d, %d, %d, %d, %d\n",
6169            stored_player[0].MovDir,
6170            stored_player[0].MovPos,
6171            stored_player[0].GfxPos,
6172            stored_player[0].Frame,
6173            stored_player[0].StepFrame);
6174 #endif
6175
6176 #if 1
6177   FrameCounter++;
6178   TimeFrames++;
6179
6180   for (i=0; i<MAX_PLAYERS; i++)
6181   {
6182     int move_frames =
6183       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
6184
6185     stored_player[i].Frame += move_frames;
6186
6187     if (stored_player[i].MovPos != 0)
6188       stored_player[i].StepFrame += move_frames;
6189   }
6190 #endif
6191 }
6192
6193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
6194 {
6195   int min_x = x, min_y = y, max_x = x, max_y = y;
6196   int i;
6197
6198   for (i=0; i<MAX_PLAYERS; i++)
6199   {
6200     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6201
6202     if (!stored_player[i].active || &stored_player[i] == player)
6203       continue;
6204
6205     min_x = MIN(min_x, jx);
6206     min_y = MIN(min_y, jy);
6207     max_x = MAX(max_x, jx);
6208     max_y = MAX(max_y, jy);
6209   }
6210
6211   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
6212 }
6213
6214 static boolean AllPlayersInVisibleScreen()
6215 {
6216   int i;
6217
6218   for (i=0; i<MAX_PLAYERS; i++)
6219   {
6220     int jx = stored_player[i].jx, jy = stored_player[i].jy;
6221
6222     if (!stored_player[i].active)
6223       continue;
6224
6225     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6226       return FALSE;
6227   }
6228
6229   return TRUE;
6230 }
6231
6232 void ScrollLevel(int dx, int dy)
6233 {
6234   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
6235   int x, y;
6236
6237   BlitBitmap(drawto_field, drawto_field,
6238              FX + TILEX * (dx == -1) - softscroll_offset,
6239              FY + TILEY * (dy == -1) - softscroll_offset,
6240              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
6241              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
6242              FX + TILEX * (dx == 1) - softscroll_offset,
6243              FY + TILEY * (dy == 1) - softscroll_offset);
6244
6245   if (dx)
6246   {
6247     x = (dx == 1 ? BX1 : BX2);
6248     for (y=BY1; y <= BY2; y++)
6249       DrawScreenField(x, y);
6250   }
6251
6252   if (dy)
6253   {
6254     y = (dy == 1 ? BY1 : BY2);
6255     for (x=BX1; x <= BX2; x++)
6256       DrawScreenField(x, y);
6257   }
6258
6259   redraw_mask |= REDRAW_FIELD;
6260 }
6261
6262 static void CheckGravityMovement(struct PlayerInfo *player)
6263 {
6264   if (level.gravity && !player->programmed_action)
6265   {
6266     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
6267     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
6268     int move_dir =
6269       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
6270        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
6271        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
6272     int jx = player->jx, jy = player->jy;
6273     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
6274     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
6275     int new_jx = jx + dx, new_jy = jy + dy;
6276     boolean field_under_player_is_free =
6277       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
6278     boolean player_is_moving_to_valid_field =
6279       (IN_LEV_FIELD(new_jx, new_jy) &&
6280        (Feld[new_jx][new_jy] == EL_SP_BASE ||
6281         Feld[new_jx][new_jy] == EL_SAND));
6282     /* !!! extend EL_SAND to anything diggable !!! */
6283
6284     if (field_under_player_is_free &&
6285         !player_is_moving_to_valid_field &&
6286         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
6287       player->programmed_action = MV_DOWN;
6288   }
6289 }
6290
6291 /*
6292   MoveFigureOneStep()
6293   -----------------------------------------------------------------------------
6294   dx, dy:               direction (non-diagonal) to try to move the player to
6295   real_dx, real_dy:     direction as read from input device (can be diagonal)
6296 */
6297
6298 boolean MoveFigureOneStep(struct PlayerInfo *player,
6299                           int dx, int dy, int real_dx, int real_dy)
6300 {
6301   int jx = player->jx, jy = player->jy;
6302   int new_jx = jx+dx, new_jy = jy+dy;
6303   int element;
6304   int can_move;
6305
6306   if (!player->active || (!dx && !dy))
6307     return MF_NO_ACTION;
6308
6309   player->MovDir = (dx < 0 ? MV_LEFT :
6310                     dx > 0 ? MV_RIGHT :
6311                     dy < 0 ? MV_UP :
6312                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
6313
6314   if (!IN_LEV_FIELD(new_jx, new_jy))
6315     return MF_NO_ACTION;
6316
6317   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
6318     return MF_NO_ACTION;
6319
6320 #if 0
6321   element = MovingOrBlocked2Element(new_jx, new_jy);
6322 #else
6323   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
6324 #endif
6325
6326   if (DONT_RUN_INTO(element))
6327   {
6328     if (element == EL_ACID && dx == 0 && dy == 1)
6329     {
6330       SplashAcid(jx, jy);
6331       Feld[jx][jy] = EL_PLAYER_1;
6332       InitMovingField(jx, jy, MV_DOWN);
6333       Store[jx][jy] = EL_ACID;
6334       ContinueMoving(jx, jy);
6335       BuryHero(player);
6336     }
6337     else
6338       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
6339
6340     return MF_MOVING;
6341   }
6342
6343   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
6344   if (can_move != MF_MOVING)
6345     return can_move;
6346
6347   /* check if DigField() has caused relocation of the player */
6348   if (player->jx != jx || player->jy != jy)
6349     return MF_NO_ACTION;
6350
6351   StorePlayer[jx][jy] = 0;
6352   player->last_jx = jx;
6353   player->last_jy = jy;
6354   jx = player->jx = new_jx;
6355   jy = player->jy = new_jy;
6356   StorePlayer[jx][jy] = player->element_nr;
6357
6358   player->MovPos =
6359     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
6360
6361   ScrollFigure(player, SCROLL_INIT);
6362
6363   return MF_MOVING;
6364 }
6365
6366 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
6367 {
6368   int jx = player->jx, jy = player->jy;
6369   int old_jx = jx, old_jy = jy;
6370   int moved = MF_NO_ACTION;
6371
6372   if (!player->active || (!dx && !dy))
6373     return FALSE;
6374
6375 #if 0
6376   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6377       !tape.playing)
6378     return FALSE;
6379 #else
6380   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
6381       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
6382     return FALSE;
6383 #endif
6384
6385   /* remove the last programmed player action */
6386   player->programmed_action = 0;
6387
6388   if (player->MovPos)
6389   {
6390     /* should only happen if pre-1.2 tape recordings are played */
6391     /* this is only for backward compatibility */
6392
6393     int original_move_delay_value = player->move_delay_value;
6394
6395 #if DEBUG
6396     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
6397            tape.counter);
6398 #endif
6399
6400     /* scroll remaining steps with finest movement resolution */
6401     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
6402
6403     while (player->MovPos)
6404     {
6405       ScrollFigure(player, SCROLL_GO_ON);
6406       ScrollScreen(NULL, SCROLL_GO_ON);
6407       FrameCounter++;
6408       DrawAllPlayers();
6409       BackToFront();
6410     }
6411
6412     player->move_delay_value = original_move_delay_value;
6413   }
6414
6415   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
6416   {
6417     if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
6418       moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
6419   }
6420   else
6421   {
6422     if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
6423       moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
6424   }
6425
6426   jx = player->jx;
6427   jy = player->jy;
6428
6429   if (moved & MF_MOVING && !ScreenMovPos &&
6430       (player == local_player || !options.network))
6431   {
6432     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
6433     int offset = (setup.scroll_delay ? 3 : 0);
6434
6435     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
6436     {
6437       /* actual player has left the screen -- scroll in that direction */
6438       if (jx != old_jx)         /* player has moved horizontally */
6439         scroll_x += (jx - old_jx);
6440       else                      /* player has moved vertically */
6441         scroll_y += (jy - old_jy);
6442     }
6443     else
6444     {
6445       if (jx != old_jx)         /* player has moved horizontally */
6446       {
6447         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
6448             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
6449           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
6450
6451         /* don't scroll over playfield boundaries */
6452         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
6453           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
6454
6455         /* don't scroll more than one field at a time */
6456         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
6457
6458         /* don't scroll against the player's moving direction */
6459         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
6460             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
6461           scroll_x = old_scroll_x;
6462       }
6463       else                      /* player has moved vertically */
6464       {
6465         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
6466             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
6467           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
6468
6469         /* don't scroll over playfield boundaries */
6470         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
6471           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
6472
6473         /* don't scroll more than one field at a time */
6474         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
6475
6476         /* don't scroll against the player's moving direction */
6477         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
6478             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
6479           scroll_y = old_scroll_y;
6480       }
6481     }
6482
6483     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
6484     {
6485       if (!options.network && !AllPlayersInVisibleScreen())
6486       {
6487         scroll_x = old_scroll_x;
6488         scroll_y = old_scroll_y;
6489       }
6490       else
6491       {
6492         ScrollScreen(player, SCROLL_INIT);
6493         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
6494       }
6495     }
6496   }
6497
6498 #if 0
6499 #if 1
6500   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
6501 #else
6502   if (!(moved & MF_MOVING) && !player->Pushing)
6503     player->Frame = 0;
6504 #endif
6505 #endif
6506
6507   player->StepFrame = 0;
6508
6509   if (moved & MF_MOVING)
6510   {
6511     if (old_jx != jx && old_jy == jy)
6512       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
6513     else if (old_jx == jx && old_jy != jy)
6514       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
6515
6516     DrawLevelField(jx, jy);     /* for "crumbled sand" */
6517
6518     player->last_move_dir = player->MovDir;
6519     player->is_moving = TRUE;
6520   }
6521   else
6522   {
6523     CheckGravityMovement(player);
6524
6525     /*
6526     player->last_move_dir = MV_NO_MOVING;
6527     */
6528     player->is_moving = FALSE;
6529   }
6530
6531   TestIfHeroTouchesBadThing(jx, jy);
6532   TestIfPlayerTouchesCustomElement(jx, jy);
6533
6534   if (!player->active)
6535     RemoveHero(player);
6536
6537   return moved;
6538 }
6539
6540 void ScrollFigure(struct PlayerInfo *player, int mode)
6541 {
6542   int jx = player->jx, jy = player->jy;
6543   int last_jx = player->last_jx, last_jy = player->last_jy;
6544   int move_stepsize = TILEX / player->move_delay_value;
6545
6546   if (!player->active || !player->MovPos)
6547     return;
6548
6549   if (mode == SCROLL_INIT)
6550   {
6551     player->actual_frame_counter = FrameCounter;
6552     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6553
6554     if (Feld[last_jx][last_jy] == EL_EMPTY)
6555       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
6556
6557 #if 0
6558     DrawPlayer(player);
6559 #endif
6560     return;
6561   }
6562   else if (!FrameReached(&player->actual_frame_counter, 1))
6563     return;
6564
6565   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
6566   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
6567
6568   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
6569     Feld[last_jx][last_jy] = EL_EMPTY;
6570
6571   /* before DrawPlayer() to draw correct player graphic for this case */
6572   if (player->MovPos == 0)
6573     CheckGravityMovement(player);
6574
6575 #if 0
6576   DrawPlayer(player);   /* needed here only to cleanup last field */
6577 #endif
6578
6579   if (player->MovPos == 0)
6580   {
6581     if (IS_PASSABLE(Feld[last_jx][last_jy]))
6582     {
6583       /* continue with normal speed after quickly moving through gate */
6584       HALVE_PLAYER_SPEED(player);
6585
6586       /* be able to make the next move without delay */
6587       player->move_delay = 0;
6588     }
6589
6590     player->last_jx = jx;
6591     player->last_jy = jy;
6592
6593     if (Feld[jx][jy] == EL_EXIT_OPEN ||
6594         Feld[jx][jy] == EL_SP_EXIT_OPEN)
6595     {
6596       DrawPlayer(player);       /* needed here only to cleanup last field */
6597       RemoveHero(player);
6598
6599       if (local_player->friends_still_needed == 0 ||
6600           Feld[jx][jy] == EL_SP_EXIT_OPEN)
6601         player->LevelSolved = player->GameOver = TRUE;
6602     }
6603
6604     if (tape.single_step && tape.recording && !tape.pausing &&
6605         !player->programmed_action)
6606       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6607   }
6608 }
6609
6610 void ScrollScreen(struct PlayerInfo *player, int mode)
6611 {
6612   static unsigned long screen_frame_counter = 0;
6613
6614   if (mode == SCROLL_INIT)
6615   {
6616     /* set scrolling step size according to actual player's moving speed */
6617     ScrollStepSize = TILEX / player->move_delay_value;
6618
6619     screen_frame_counter = FrameCounter;
6620     ScreenMovDir = player->MovDir;
6621     ScreenMovPos = player->MovPos;
6622     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6623     return;
6624   }
6625   else if (!FrameReached(&screen_frame_counter, 1))
6626     return;
6627
6628   if (ScreenMovPos)
6629   {
6630     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
6631     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
6632     redraw_mask |= REDRAW_FIELD;
6633   }
6634   else
6635     ScreenMovDir = MV_NO_MOVING;
6636 }
6637
6638 void TestIfPlayerTouchesCustomElement(int x, int y)
6639 {
6640   static boolean check_changing = FALSE;
6641   static int xy[4][2] =
6642   {
6643     { 0, -1 },
6644     { -1, 0 },
6645     { +1, 0 },
6646     { 0, +1 }
6647   };
6648   int i;
6649
6650   if (check_changing)   /* prevent this function from running into a loop */
6651     return;
6652
6653   check_changing = TRUE;
6654
6655   for (i=0; i<4; i++)
6656   {
6657     int xx = x + xy[i][0];
6658     int yy = y + xy[i][1];
6659
6660     if (!IN_LEV_FIELD(xx, yy))
6661       continue;
6662
6663     if (IS_PLAYER(x, y))
6664     {
6665       CheckTriggeredElementChange(xx, yy, Feld[xx][yy], CE_OTHER_GETS_TOUCHED);
6666       CheckElementChange(xx, yy, Feld[xx][yy], CE_TOUCHED_BY_PLAYER);
6667     }
6668     else if (IS_PLAYER(xx, yy))
6669     {
6670       CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_GETS_TOUCHED);
6671       CheckElementChange(x, y, Feld[x][y], CE_TOUCHED_BY_PLAYER);
6672
6673       break;
6674     }
6675   }
6676
6677   check_changing = FALSE;
6678 }
6679
6680 void TestIfElementTouchesCustomElement(int x, int y)
6681 {
6682   static boolean check_changing = FALSE;
6683   static int xy[4][2] =
6684   {
6685     { 0, -1 },
6686     { -1, 0 },
6687     { +1, 0 },
6688     { 0, +1 }
6689   };
6690   boolean change_center_element = FALSE;
6691   int center_element = Feld[x][y];
6692   int i;
6693
6694   if (check_changing)   /* prevent this function from running into a loop */
6695     return;
6696
6697   check_changing = TRUE;
6698
6699   for (i=0; i<4; i++)
6700   {
6701     int xx = x + xy[i][0];
6702     int yy = y + xy[i][1];
6703     int border_element;
6704
6705     if (!IN_LEV_FIELD(xx, yy))
6706       continue;
6707
6708     border_element = Feld[xx][yy];
6709
6710     /* check for change of center element (but change it only once) */
6711     if (IS_CUSTOM_ELEMENT(center_element) &&
6712         border_element == element_info[center_element].change.trigger_element)
6713       change_center_element = TRUE;
6714
6715     /* check for change of border element */
6716     if (IS_CUSTOM_ELEMENT(border_element) &&
6717         center_element == element_info[border_element].change.trigger_element)
6718       CheckElementChange(xx, yy, border_element, CE_OTHER_IS_TOUCHING);
6719   }
6720
6721   if (change_center_element)
6722     CheckElementChange(x, y, center_element, CE_OTHER_IS_TOUCHING);
6723
6724   check_changing = FALSE;
6725 }
6726
6727 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
6728 {
6729   int i, kill_x = -1, kill_y = -1;
6730   static int test_xy[4][2] =
6731   {
6732     { 0, -1 },
6733     { -1, 0 },
6734     { +1, 0 },
6735     { 0, +1 }
6736   };
6737   static int test_dir[4] =
6738   {
6739     MV_UP,
6740     MV_LEFT,
6741     MV_RIGHT,
6742     MV_DOWN
6743   };
6744
6745   for (i=0; i<4; i++)
6746   {
6747     int test_x, test_y, test_move_dir, test_element;
6748
6749     test_x = good_x + test_xy[i][0];
6750     test_y = good_y + test_xy[i][1];
6751     if (!IN_LEV_FIELD(test_x, test_y))
6752       continue;
6753
6754     test_move_dir =
6755       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
6756
6757 #if 0
6758     test_element = Feld[test_x][test_y];
6759 #else
6760     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
6761 #endif
6762
6763     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
6764        2nd case: DONT_TOUCH style bad thing does not move away from good thing
6765     */
6766     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
6767         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
6768     {
6769       kill_x = test_x;
6770       kill_y = test_y;
6771       break;
6772     }
6773   }
6774
6775   if (kill_x != -1 || kill_y != -1)
6776   {
6777     if (IS_PLAYER(good_x, good_y))
6778     {
6779       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
6780
6781       if (player->shield_deadly_time_left > 0)
6782         Bang(kill_x, kill_y);
6783       else if (!PLAYER_PROTECTED(good_x, good_y))
6784         KillHero(player);
6785     }
6786     else
6787       Bang(good_x, good_y);
6788   }
6789 }
6790
6791 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
6792 {
6793   int i, kill_x = -1, kill_y = -1;
6794   int bad_element = Feld[bad_x][bad_y];
6795   static int test_xy[4][2] =
6796   {
6797     { 0, -1 },
6798     { -1, 0 },
6799     { +1, 0 },
6800     { 0, +1 }
6801   };
6802   static int test_dir[4] =
6803   {
6804     MV_UP,
6805     MV_LEFT,
6806     MV_RIGHT,
6807     MV_DOWN
6808   };
6809
6810   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
6811     return;
6812
6813   for (i=0; i<4; i++)
6814   {
6815     int test_x, test_y, test_move_dir, test_element;
6816
6817     test_x = bad_x + test_xy[i][0];
6818     test_y = bad_y + test_xy[i][1];
6819     if (!IN_LEV_FIELD(test_x, test_y))
6820       continue;
6821
6822     test_move_dir =
6823       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
6824
6825     test_element = Feld[test_x][test_y];
6826
6827     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
6828        2nd case: DONT_TOUCH style bad thing does not move away from good thing
6829     */
6830     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
6831         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
6832     {
6833       /* good thing is player or penguin that does not move away */
6834       if (IS_PLAYER(test_x, test_y))
6835       {
6836         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
6837
6838         if (bad_element == EL_ROBOT && player->is_moving)
6839           continue;     /* robot does not kill player if he is moving */
6840
6841         kill_x = test_x;
6842         kill_y = test_y;
6843         break;
6844       }
6845       else if (test_element == EL_PENGUIN)
6846       {
6847         kill_x = test_x;
6848         kill_y = test_y;
6849         break;
6850       }
6851     }
6852   }
6853
6854   if (kill_x != -1 || kill_y != -1)
6855   {
6856     if (IS_PLAYER(kill_x, kill_y))
6857     {
6858       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
6859
6860 #if 0
6861       int dir = player->MovDir;
6862       int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6863       int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6864
6865       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
6866           newx != bad_x && newy != bad_y)
6867         ;       /* robot does not kill player if he is moving */
6868       else
6869         printf("-> %d\n", player->MovDir);
6870
6871       if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
6872           newx != bad_x && newy != bad_y)
6873         ;       /* robot does not kill player if he is moving */
6874       else
6875         ;
6876 #endif
6877
6878       if (player->shield_deadly_time_left > 0)
6879         Bang(bad_x, bad_y);
6880       else if (!PLAYER_PROTECTED(kill_x, kill_y))
6881         KillHero(player);
6882     }
6883     else
6884       Bang(kill_x, kill_y);
6885   }
6886 }
6887
6888 void TestIfHeroTouchesBadThing(int x, int y)
6889 {
6890   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
6891 }
6892
6893 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
6894 {
6895   TestIfGoodThingHitsBadThing(x, y, move_dir);
6896 }
6897
6898 void TestIfBadThingTouchesHero(int x, int y)
6899 {
6900   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
6901 }
6902
6903 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
6904 {
6905   TestIfBadThingHitsGoodThing(x, y, move_dir);
6906 }
6907
6908 void TestIfFriendTouchesBadThing(int x, int y)
6909 {
6910   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
6911 }
6912
6913 void TestIfBadThingTouchesFriend(int x, int y)
6914 {
6915   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
6916 }
6917
6918 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
6919 {
6920   int i, kill_x = bad_x, kill_y = bad_y;
6921   static int xy[4][2] =
6922   {
6923     { 0, -1 },
6924     { -1, 0 },
6925     { +1, 0 },
6926     { 0, +1 }
6927   };
6928
6929   for (i=0; i<4; i++)
6930   {
6931     int x, y, element;
6932
6933     x = bad_x + xy[i][0];
6934     y = bad_y + xy[i][1];
6935     if (!IN_LEV_FIELD(x, y))
6936       continue;
6937
6938     element = Feld[x][y];
6939     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
6940         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
6941     {
6942       kill_x = x;
6943       kill_y = y;
6944       break;
6945     }
6946   }
6947
6948   if (kill_x != bad_x || kill_y != bad_y)
6949     Bang(bad_x, bad_y);
6950 }
6951
6952 void KillHero(struct PlayerInfo *player)
6953 {
6954   int jx = player->jx, jy = player->jy;
6955
6956   if (!player->active)
6957     return;
6958
6959   /* remove accessible field at the player's position */
6960   Feld[jx][jy] = EL_EMPTY;
6961
6962   /* deactivate shield (else Bang()/Explode() would not work right) */
6963   player->shield_normal_time_left = 0;
6964   player->shield_deadly_time_left = 0;
6965
6966   Bang(jx, jy);
6967   BuryHero(player);
6968 }
6969
6970 static void KillHeroUnlessProtected(int x, int y)
6971 {
6972   if (!PLAYER_PROTECTED(x, y))
6973     KillHero(PLAYERINFO(x, y));
6974 }
6975
6976 void BuryHero(struct PlayerInfo *player)
6977 {
6978   int jx = player->jx, jy = player->jy;
6979
6980   if (!player->active)
6981     return;
6982
6983 #if 1
6984   PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
6985 #else
6986   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
6987 #endif
6988   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
6989
6990   player->GameOver = TRUE;
6991   RemoveHero(player);
6992 }
6993
6994 void RemoveHero(struct PlayerInfo *player)
6995 {
6996   int jx = player->jx, jy = player->jy;
6997   int i, found = FALSE;
6998
6999   player->present = FALSE;
7000   player->active = FALSE;
7001
7002   if (!ExplodeField[jx][jy])
7003     StorePlayer[jx][jy] = 0;
7004
7005   for (i=0; i<MAX_PLAYERS; i++)
7006     if (stored_player[i].active)
7007       found = TRUE;
7008
7009   if (!found)
7010     AllPlayersGone = TRUE;
7011
7012   ExitX = ZX = jx;
7013   ExitY = ZY = jy;
7014 }
7015
7016 /*
7017   =============================================================================
7018   checkDiagonalPushing()
7019   -----------------------------------------------------------------------------
7020   check if diagonal input device direction results in pushing of object
7021   (by checking if the alternative direction is walkable, diggable, ...)
7022   =============================================================================
7023 */
7024
7025 static boolean checkDiagonalPushing(struct PlayerInfo *player,
7026                                     int x, int y, int real_dx, int real_dy)
7027 {
7028   int jx, jy, dx, dy, xx, yy;
7029
7030   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
7031     return TRUE;
7032
7033   /* diagonal direction: check alternative direction */
7034   jx = player->jx;
7035   jy = player->jy;
7036   dx = x - jx;
7037   dy = y - jy;
7038   xx = jx + (dx == 0 ? real_dx : 0);
7039   yy = jy + (dy == 0 ? real_dy : 0);
7040
7041   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
7042 }
7043
7044 /*
7045   =============================================================================
7046   DigField()
7047   -----------------------------------------------------------------------------
7048   x, y:                 field next to player (non-diagonal) to try to dig to
7049   real_dx, real_dy:     direction as read from input device (can be diagonal)
7050   =============================================================================
7051 */
7052
7053 int DigField(struct PlayerInfo *player,
7054              int x, int y, int real_dx, int real_dy, int mode)
7055 {
7056   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
7057   int jx = player->jx, jy = player->jy;
7058   int dx = x - jx, dy = y - jy;
7059   int nextx = x + dx, nexty = y + dy;
7060   int move_direction = (dx == -1 ? MV_LEFT :
7061                         dx == +1 ? MV_RIGHT :
7062                         dy == -1 ? MV_UP :
7063                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7064   int element;
7065
7066   if (player->MovPos == 0)
7067   {
7068     player->is_digging = FALSE;
7069     player->is_collecting = FALSE;
7070   }
7071
7072   if (player->MovPos == 0)      /* last pushing move finished */
7073     player->Pushing = FALSE;
7074
7075   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
7076   {
7077     player->Switching = FALSE;
7078     player->push_delay = 0;
7079
7080     return MF_NO_ACTION;
7081   }
7082
7083   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
7084     return MF_NO_ACTION;
7085
7086 #if 0
7087   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
7088 #else
7089   if (IS_TUBE(Feld[jx][jy]) ||
7090       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
7091 #endif
7092   {
7093     int i = 0;
7094     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
7095     int tube_leave_directions[][2] =
7096     {
7097       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7098       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
7099       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
7100       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
7101       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
7102       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
7103       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
7104       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
7105       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
7106       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
7107       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
7108       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
7109     };
7110
7111     while (tube_leave_directions[i][0] != tube_element)
7112     {
7113       i++;
7114       if (tube_leave_directions[i][0] == -1)    /* should not happen */
7115         break;
7116     }
7117
7118     if (!(tube_leave_directions[i][1] & move_direction))
7119       return MF_NO_ACTION;      /* tube has no opening in this direction */
7120   }
7121
7122   element = Feld[x][y];
7123
7124   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
7125       game.engine_version >= VERSION_IDENT(2,2,0))
7126     return MF_NO_ACTION;
7127
7128   switch (element)
7129   {
7130     case EL_ROBOT_WHEEL:
7131       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
7132       ZX = x;
7133       ZY = y;
7134       DrawLevelField(x, y);
7135       PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING);
7136       return MF_ACTION;
7137       break;
7138
7139     case EL_SP_TERMINAL:
7140       {
7141         int xx, yy;
7142
7143         PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING);
7144
7145         for (yy=0; yy<lev_fieldy; yy++)
7146         {
7147           for (xx=0; xx<lev_fieldx; xx++)
7148           {
7149             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
7150               Bang(xx, yy);
7151             else if (Feld[xx][yy] == EL_SP_TERMINAL)
7152               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
7153           }
7154         }
7155
7156         return MF_ACTION;
7157       }
7158       break;
7159
7160     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
7161     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
7162     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
7163     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
7164     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
7165     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
7166     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
7167     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
7168     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
7169     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
7170     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
7171     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
7172       if (!player->Switching)
7173       {
7174         player->Switching = TRUE;
7175         ToggleBeltSwitch(x, y);
7176         PlaySoundLevel(x, y, SND_CLASS_CONVEYOR_BELT_SWITCH_ACTIVATING);
7177       }
7178       return MF_ACTION;
7179       break;
7180
7181     case EL_SWITCHGATE_SWITCH_UP:
7182     case EL_SWITCHGATE_SWITCH_DOWN:
7183       if (!player->Switching)
7184       {
7185         player->Switching = TRUE;
7186         ToggleSwitchgateSwitch(x, y);
7187         PlaySoundLevel(x, y, SND_CLASS_SWITCHGATE_SWITCH_ACTIVATING);
7188       }
7189       return MF_ACTION;
7190       break;
7191
7192     case EL_LIGHT_SWITCH:
7193     case EL_LIGHT_SWITCH_ACTIVE:
7194       if (!player->Switching)
7195       {
7196         player->Switching = TRUE;
7197         ToggleLightSwitch(x, y);
7198         PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH ?
7199                        SND_LIGHT_SWITCH_ACTIVATING :
7200                        SND_LIGHT_SWITCH_DEACTIVATING);
7201       }
7202       return MF_ACTION;
7203       break;
7204
7205     case EL_TIMEGATE_SWITCH:
7206       ActivateTimegateSwitch(x, y);
7207       PlaySoundLevel(x, y, SND_TIMEGATE_SWITCH_ACTIVATING);
7208
7209       return MF_ACTION;
7210       break;
7211
7212     case EL_BALLOON_SWITCH_LEFT:
7213     case EL_BALLOON_SWITCH_RIGHT:
7214     case EL_BALLOON_SWITCH_UP:
7215     case EL_BALLOON_SWITCH_DOWN:
7216     case EL_BALLOON_SWITCH_ANY:
7217       if (element == EL_BALLOON_SWITCH_ANY)
7218         game.balloon_dir = move_direction;
7219       else
7220         game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
7221                             element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
7222                             element == EL_BALLOON_SWITCH_UP    ? MV_UP :
7223                             element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
7224                             MV_NO_MOVING);
7225       PlaySoundLevel(x, y, SND_CLASS_BALLOON_SWITCH_ACTIVATING);
7226
7227       return MF_ACTION;
7228       break;
7229
7230     case EL_SP_PORT_LEFT:
7231     case EL_SP_PORT_RIGHT:
7232     case EL_SP_PORT_UP:
7233     case EL_SP_PORT_DOWN:
7234     case EL_SP_PORT_HORIZONTAL:
7235     case EL_SP_PORT_VERTICAL:
7236     case EL_SP_PORT_ANY:
7237     case EL_SP_GRAVITY_PORT_LEFT:
7238     case EL_SP_GRAVITY_PORT_RIGHT:
7239     case EL_SP_GRAVITY_PORT_UP:
7240     case EL_SP_GRAVITY_PORT_DOWN:
7241       if ((dx == -1 &&
7242            element != EL_SP_PORT_LEFT &&
7243            element != EL_SP_GRAVITY_PORT_LEFT &&
7244            element != EL_SP_PORT_HORIZONTAL &&
7245            element != EL_SP_PORT_ANY) ||
7246           (dx == +1 &&
7247            element != EL_SP_PORT_RIGHT &&
7248            element != EL_SP_GRAVITY_PORT_RIGHT &&
7249            element != EL_SP_PORT_HORIZONTAL &&
7250            element != EL_SP_PORT_ANY) ||
7251           (dy == -1 &&
7252            element != EL_SP_PORT_UP &&
7253            element != EL_SP_GRAVITY_PORT_UP &&
7254            element != EL_SP_PORT_VERTICAL &&
7255            element != EL_SP_PORT_ANY) ||
7256           (dy == +1 &&
7257            element != EL_SP_PORT_DOWN &&
7258            element != EL_SP_GRAVITY_PORT_DOWN &&
7259            element != EL_SP_PORT_VERTICAL &&
7260            element != EL_SP_PORT_ANY) ||
7261           !IN_LEV_FIELD(nextx, nexty) ||
7262           !IS_FREE(nextx, nexty))
7263         return MF_NO_ACTION;
7264
7265       if (element == EL_SP_GRAVITY_PORT_LEFT ||
7266           element == EL_SP_GRAVITY_PORT_RIGHT ||
7267           element == EL_SP_GRAVITY_PORT_UP ||
7268           element == EL_SP_GRAVITY_PORT_DOWN)
7269         level.gravity = !level.gravity;
7270
7271       /* automatically move to the next field with double speed */
7272       player->programmed_action = move_direction;
7273       DOUBLE_PLAYER_SPEED(player);
7274
7275       PlaySoundLevel(x, y, SND_CLASS_SP_PORT_PASSING);
7276       break;
7277
7278     case EL_TUBE_ANY:
7279     case EL_TUBE_VERTICAL:
7280     case EL_TUBE_HORIZONTAL:
7281     case EL_TUBE_VERTICAL_LEFT:
7282     case EL_TUBE_VERTICAL_RIGHT:
7283     case EL_TUBE_HORIZONTAL_UP:
7284     case EL_TUBE_HORIZONTAL_DOWN:
7285     case EL_TUBE_LEFT_UP:
7286     case EL_TUBE_LEFT_DOWN:
7287     case EL_TUBE_RIGHT_UP:
7288     case EL_TUBE_RIGHT_DOWN:
7289       {
7290         int i = 0;
7291         int tube_enter_directions[][2] =
7292         {
7293           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
7294           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
7295           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
7296           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
7297           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
7298           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
7299           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
7300           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
7301           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
7302           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
7303           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
7304           { -1,                         MV_NO_MOVING                         }
7305         };
7306
7307         while (tube_enter_directions[i][0] != element)
7308         {
7309           i++;
7310           if (tube_enter_directions[i][0] == -1)        /* should not happen */
7311             break;
7312         }
7313
7314         if (!(tube_enter_directions[i][1] & move_direction))
7315           return MF_NO_ACTION;  /* tube has no opening in this direction */
7316
7317         PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
7318       }
7319       break;
7320
7321     case EL_LAMP:
7322       Feld[x][y] = EL_LAMP_ACTIVE;
7323       local_player->lights_still_needed--;
7324       DrawLevelField(x, y);
7325       PlaySoundLevel(x, y, SND_LAMP_ACTIVATING);
7326       return MF_ACTION;
7327       break;
7328
7329     case EL_TIME_ORB_FULL:
7330       Feld[x][y] = EL_TIME_ORB_EMPTY;
7331       TimeLeft += 10;
7332       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7333       DrawLevelField(x, y);
7334       PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
7335       return MF_ACTION;
7336       break;
7337
7338     default:
7339
7340       if (IS_WALKABLE(element))
7341       {
7342         int sound_action = ACTION_WALKING;
7343
7344         if (element >= EL_GATE_1 && element <= EL_GATE_4)
7345         {
7346           if (!player->key[element - EL_GATE_1])
7347             return MF_NO_ACTION;
7348         }
7349         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
7350         {
7351           if (!player->key[element - EL_GATE_1_GRAY])
7352             return MF_NO_ACTION;
7353         }
7354         else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN)
7355         {
7356           sound_action = ACTION_PASSING;        /* player is passing exit */
7357         }
7358         else if (element == EL_EMPTY)
7359         {
7360           sound_action = ACTION_MOVING;         /* nothing to walk on */
7361         }
7362
7363         /* play sound from background or player, whatever is available */
7364         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
7365           PlaySoundLevelElementAction(x, y, element, sound_action);
7366         else
7367           PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
7368
7369         break;
7370       }
7371       else if (IS_PASSABLE(element))
7372       {
7373         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
7374           return MF_NO_ACTION;
7375
7376         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
7377         {
7378           if (!player->key[element - EL_EM_GATE_1])
7379             return MF_NO_ACTION;
7380         }
7381         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
7382         {
7383           if (!player->key[element - EL_EM_GATE_1_GRAY])
7384             return MF_NO_ACTION;
7385         }
7386
7387         /* automatically move to the next field with double speed */
7388         player->programmed_action = move_direction;
7389         DOUBLE_PLAYER_SPEED(player);
7390
7391         PlaySoundLevelAction(x, y, ACTION_PASSING);
7392
7393         break;
7394       }
7395       else if (IS_DIGGABLE(element))
7396       {
7397         RemoveField(x, y);
7398
7399         if (mode != DF_SNAP)
7400         {
7401 #if 1
7402           GfxElement[x][y] = GFX_ELEMENT(element);
7403 #else
7404           GfxElement[x][y] =
7405             (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
7406 #endif
7407           player->is_digging = TRUE;
7408         }
7409
7410         PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
7411
7412         break;
7413       }
7414       else if (IS_COLLECTIBLE(element))
7415       {
7416         RemoveField(x, y);
7417
7418         if (mode != DF_SNAP)
7419         {
7420           GfxElement[x][y] = element;
7421           player->is_collecting = TRUE;
7422         }
7423
7424         if (element == EL_SPEED_PILL)
7425           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
7426         else if (element == EL_EXTRA_TIME && level.time > 0)
7427         {
7428           TimeLeft += 10;
7429           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7430         }
7431         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
7432         {
7433           player->shield_normal_time_left += 10;
7434           if (element == EL_SHIELD_DEADLY)
7435             player->shield_deadly_time_left += 10;
7436         }
7437         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
7438         {
7439           if (player->inventory_size < MAX_INVENTORY_SIZE)
7440             player->inventory_element[player->inventory_size++] = element;
7441
7442           player->use_disk_red_graphic = (element == EL_SP_DISK_RED);
7443
7444           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7445                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7446         }
7447         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
7448         {
7449           player->dynabomb_count++;
7450           player->dynabombs_left++;
7451         }
7452         else if (element == EL_DYNABOMB_INCREASE_SIZE)
7453         {
7454           player->dynabomb_size++;
7455         }
7456         else if (element == EL_DYNABOMB_INCREASE_POWER)
7457         {
7458           player->dynabomb_xl = TRUE;
7459         }
7460         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
7461                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
7462         {
7463           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
7464                         element - EL_KEY_1 : element - EL_EM_KEY_1);
7465
7466           player->key[key_nr] = TRUE;
7467
7468           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
7469                              el2edimg(EL_KEY_1 + key_nr));
7470           redraw_mask |= REDRAW_DOOR_1;
7471         }
7472         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
7473         {
7474           int i;
7475
7476           for (i=0; i < element_info[element].collect_count; i++)
7477             if (player->inventory_size < MAX_INVENTORY_SIZE)
7478               player->inventory_element[player->inventory_size++] = element;
7479
7480           DrawText(DX_DYNAMITE, DY_DYNAMITE,
7481                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7482         }
7483         else if (element_info[element].collect_count > 0)
7484         {
7485           local_player->gems_still_needed -=
7486             element_info[element].collect_count;
7487           if (local_player->gems_still_needed < 0)
7488             local_player->gems_still_needed = 0;
7489
7490           DrawText(DX_EMERALDS, DY_EMERALDS,
7491                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
7492         }
7493
7494         RaiseScoreElement(element);
7495         PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
7496
7497         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
7498
7499         break;
7500       }
7501       else if (IS_PUSHABLE(element))
7502       {
7503         if (mode == DF_SNAP && element != EL_BD_ROCK)
7504           return MF_NO_ACTION;
7505
7506         if (CAN_FALL(element) && dy)
7507           return MF_NO_ACTION;
7508
7509         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
7510             !(element == EL_SPRING && use_spring_bug))
7511           return MF_NO_ACTION;
7512
7513         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
7514           return MF_NO_ACTION;
7515
7516         if (!player->Pushing &&
7517             game.engine_version >= RELEASE_IDENT(2,2,0,7))
7518           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7519
7520         player->Pushing = TRUE;
7521
7522         if (!(IN_LEV_FIELD(nextx, nexty) &&
7523               (IS_FREE(nextx, nexty) ||
7524                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
7525                 IS_SB_ELEMENT(element)))))
7526           return MF_NO_ACTION;
7527
7528         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
7529           return MF_NO_ACTION;
7530
7531         if (player->push_delay == 0)    /* new pushing; restart delay */
7532           player->push_delay = FrameCounter;
7533
7534         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
7535             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
7536             element != EL_SPRING && element != EL_BALLOON)
7537           return MF_NO_ACTION;
7538
7539         if (IS_SB_ELEMENT(element))
7540         {
7541           if (element == EL_SOKOBAN_FIELD_FULL)
7542           {
7543             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
7544             local_player->sokobanfields_still_needed++;
7545           }
7546
7547           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
7548           {
7549             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
7550             local_player->sokobanfields_still_needed--;
7551           }
7552
7553           Feld[x][y] = EL_SOKOBAN_OBJECT;
7554
7555           if (Back[x][y] == Back[nextx][nexty])
7556             PlaySoundLevelAction(x, y, ACTION_PUSHING);
7557           else if (Back[x][y] != 0)
7558             PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
7559                                         ACTION_EMPTYING);
7560           else
7561             PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
7562                                         ACTION_FILLING);
7563
7564           if (local_player->sokobanfields_still_needed == 0 &&
7565               game.emulation == EMU_SOKOBAN)
7566           {
7567             player->LevelSolved = player->GameOver = TRUE;
7568             PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
7569           }
7570         }
7571         else
7572           PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
7573
7574         InitMovingField(x, y, move_direction);
7575         GfxAction[x][y] = ACTION_PUSHING;
7576
7577         if (mode == DF_SNAP)
7578           ContinueMoving(x, y);
7579         else
7580           MovPos[x][y] = (dx != 0 ? dx : dy);
7581
7582         Pushed[x][y] = TRUE;
7583         Pushed[nextx][nexty] = TRUE;
7584
7585         if (game.engine_version < RELEASE_IDENT(2,2,0,7))
7586           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
7587
7588         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PUSHED);
7589         CheckElementChange(x, y, element, CE_PUSHED_BY_PLAYER);
7590
7591         break;
7592       }
7593       else
7594       {
7595         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_PRESSED);
7596         CheckElementChange(x, y, element, CE_PRESSED_BY_PLAYER);
7597       }
7598
7599       return MF_NO_ACTION;
7600   }
7601
7602   player->push_delay = 0;
7603
7604   if (Feld[x][y] != element)            /* really digged/collected something */
7605     player->is_collecting = !player->is_digging;
7606
7607   return MF_MOVING;
7608 }
7609
7610 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
7611 {
7612   int jx = player->jx, jy = player->jy;
7613   int x = jx + dx, y = jy + dy;
7614   int snap_direction = (dx == -1 ? MV_LEFT :
7615                         dx == +1 ? MV_RIGHT :
7616                         dy == -1 ? MV_UP :
7617                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7618
7619   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
7620     return FALSE;
7621
7622   if (!player->active || !IN_LEV_FIELD(x, y))
7623     return FALSE;
7624
7625   if (dx && dy)
7626     return FALSE;
7627
7628   if (!dx && !dy)
7629   {
7630     if (player->MovPos == 0)
7631       player->Pushing = FALSE;
7632
7633     player->snapped = FALSE;
7634
7635     if (player->MovPos == 0)
7636     {
7637       player->is_digging = FALSE;
7638       player->is_collecting = FALSE;
7639     }
7640
7641     return FALSE;
7642   }
7643
7644   if (player->snapped)
7645     return FALSE;
7646
7647   player->MovDir = snap_direction;
7648
7649   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
7650     return FALSE;
7651
7652   player->snapped = TRUE;
7653   player->is_digging = FALSE;
7654   player->is_collecting = FALSE;
7655
7656   DrawLevelField(x, y);
7657   BackToFront();
7658
7659   return TRUE;
7660 }
7661
7662 boolean DropElement(struct PlayerInfo *player)
7663 {
7664   int jx = player->jx, jy = player->jy;
7665   int old_element;
7666
7667   if (!player->active || player->MovPos)
7668     return FALSE;
7669
7670   old_element = Feld[jx][jy];
7671
7672   /* check if player has anything that can be dropped */
7673   if (player->inventory_size == 0 && player->dynabombs_left == 0)
7674     return FALSE;
7675
7676   /* check if anything can be dropped at the current position */
7677   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
7678     return FALSE;
7679
7680   /* collected custom elements can only be dropped on empty fields */
7681   if (player->inventory_size > 0 &&
7682       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
7683       && old_element != EL_EMPTY)
7684     return FALSE;
7685
7686   if (old_element != EL_EMPTY)
7687     Back[jx][jy] = old_element;         /* store old element on this field */
7688
7689   MovDelay[jx][jy] = 96;
7690
7691   ResetGfxAnimation(jx, jy);
7692   ResetRandomAnimationValue(jx, jy);
7693
7694   if (player->inventory_size > 0)
7695   {
7696     int new_element = player->inventory_element[--player->inventory_size];
7697
7698 #if 1
7699     Feld[jx][jy] = (new_element == EL_DYNAMITE ? EL_DYNAMITE_ACTIVE :
7700                     new_element == EL_SP_DISK_RED ? EL_SP_DISK_RED_ACTIVE :
7701                     new_element);
7702 #else
7703     Feld[jx][jy] = (player->use_disk_red_graphic ? EL_SP_DISK_RED_ACTIVE :
7704                     EL_DYNAMITE_ACTIVE);
7705 #endif
7706
7707     DrawText(DX_DYNAMITE, DY_DYNAMITE,
7708              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
7709
7710     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
7711       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
7712
7713     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
7714
7715     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
7716     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
7717   }
7718   else          /* player is dropping a dyna bomb */
7719   {
7720     player->dynabombs_left--;
7721
7722     Feld[jx][jy] =
7723       EL_DYNABOMB_PLAYER_1_ACTIVE + (player->element_nr - EL_PLAYER_1);
7724
7725     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
7726       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
7727
7728     PlaySoundLevelAction(jx, jy, ACTION_DROPPING);
7729   }
7730
7731   return TRUE;
7732 }
7733
7734 /* ------------------------------------------------------------------------- */
7735 /* game sound playing functions                                              */
7736 /* ------------------------------------------------------------------------- */
7737
7738 static int *loop_sound_frame = NULL;
7739 static int *loop_sound_volume = NULL;
7740
7741 void InitPlaySoundLevel()
7742 {
7743   int num_sounds = getSoundListSize();
7744
7745   if (loop_sound_frame != NULL)
7746     free(loop_sound_frame);
7747
7748   if (loop_sound_volume != NULL)
7749     free(loop_sound_volume);
7750
7751   loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
7752   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
7753 }
7754
7755 static void PlaySoundLevel(int x, int y, int nr)
7756 {
7757   int sx = SCREENX(x), sy = SCREENY(y);
7758   int volume, stereo_position;
7759   int max_distance = 8;
7760   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
7761
7762   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
7763       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
7764     return;
7765
7766   if (!IN_LEV_FIELD(x, y) ||
7767       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
7768       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
7769     return;
7770
7771   volume = SOUND_MAX_VOLUME;
7772
7773   if (!IN_SCR_FIELD(sx, sy))
7774   {
7775     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
7776     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
7777
7778     volume -= volume * (dx > dy ? dx : dy) / max_distance;
7779   }
7780
7781   stereo_position = (SOUND_MAX_LEFT +
7782                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
7783                      (SCR_FIELDX + 2 * max_distance));
7784
7785   if (IS_LOOP_SOUND(nr))
7786   {
7787     /* This assures that quieter loop sounds do not overwrite louder ones,
7788        while restarting sound volume comparison with each new game frame. */
7789
7790     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
7791       return;
7792
7793     loop_sound_volume[nr] = volume;
7794     loop_sound_frame[nr] = FrameCounter;
7795   }
7796
7797   PlaySoundExt(nr, volume, stereo_position, type);
7798 }
7799
7800 static void PlaySoundLevelNearest(int x, int y, int sound_action)
7801 {
7802   PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
7803                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
7804                  y < LEVELY(BY1) ? LEVELY(BY1) :
7805                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
7806                  sound_action);
7807 }
7808
7809 static void PlaySoundLevelAction(int x, int y, int action)
7810 {
7811   PlaySoundLevelElementAction(x, y, Feld[x][y], action);
7812 }
7813
7814 static void PlaySoundLevelElementAction(int x, int y, int element, int action)
7815 {
7816   int sound_effect = element_info[element].sound[action];
7817
7818   if (sound_effect != SND_UNDEFINED)
7819     PlaySoundLevel(x, y, sound_effect);
7820 }
7821
7822 static void PlaySoundLevelActionIfLoop(int x, int y, int action)
7823 {
7824   int sound_effect = element_info[Feld[x][y]].sound[action];
7825
7826   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
7827     PlaySoundLevel(x, y, sound_effect);
7828 }
7829
7830 static void StopSoundLevelActionIfLoop(int x, int y, int action)
7831 {
7832   int sound_effect = element_info[Feld[x][y]].sound[action];
7833
7834   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
7835     StopSoundExt(sound_effect, SND_CTRL_STOP_SOUND);
7836 }
7837
7838 void RaiseScore(int value)
7839 {
7840   local_player->score += value;
7841   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
7842 }
7843
7844 void RaiseScoreElement(int element)
7845 {
7846   switch(element)
7847   {
7848     case EL_EMERALD:
7849     case EL_BD_DIAMOND:
7850     case EL_EMERALD_YELLOW:
7851     case EL_EMERALD_RED:
7852     case EL_EMERALD_PURPLE:
7853     case EL_SP_INFOTRON:
7854       RaiseScore(level.score[SC_EMERALD]);
7855       break;
7856     case EL_DIAMOND:
7857       RaiseScore(level.score[SC_DIAMOND]);
7858       break;
7859     case EL_CRYSTAL:
7860       RaiseScore(level.score[SC_CRYSTAL]);
7861       break;
7862     case EL_PEARL:
7863       RaiseScore(level.score[SC_PEARL]);
7864       break;
7865     case EL_BUG:
7866     case EL_BD_BUTTERFLY:
7867     case EL_SP_ELECTRON:
7868       RaiseScore(level.score[SC_BUG]);
7869       break;
7870     case EL_SPACESHIP:
7871     case EL_BD_FIREFLY:
7872     case EL_SP_SNIKSNAK:
7873       RaiseScore(level.score[SC_SPACESHIP]);
7874       break;
7875     case EL_YAMYAM:
7876     case EL_DARK_YAMYAM:
7877       RaiseScore(level.score[SC_YAMYAM]);
7878       break;
7879     case EL_ROBOT:
7880       RaiseScore(level.score[SC_ROBOT]);
7881       break;
7882     case EL_PACMAN:
7883       RaiseScore(level.score[SC_PACMAN]);
7884       break;
7885     case EL_NUT:
7886       RaiseScore(level.score[SC_NUT]);
7887       break;
7888     case EL_DYNAMITE:
7889     case EL_SP_DISK_RED:
7890     case EL_DYNABOMB_INCREASE_NUMBER:
7891     case EL_DYNABOMB_INCREASE_SIZE:
7892     case EL_DYNABOMB_INCREASE_POWER:
7893       RaiseScore(level.score[SC_DYNAMITE]);
7894       break;
7895     case EL_SHIELD_NORMAL:
7896     case EL_SHIELD_DEADLY:
7897       RaiseScore(level.score[SC_SHIELD]);
7898       break;
7899     case EL_EXTRA_TIME:
7900       RaiseScore(level.score[SC_TIME_BONUS]);
7901       break;
7902     case EL_KEY_1:
7903     case EL_KEY_2:
7904     case EL_KEY_3:
7905     case EL_KEY_4:
7906       RaiseScore(level.score[SC_KEY]);
7907       break;
7908     default:
7909       RaiseScore(element_info[element].collect_score);
7910       break;
7911   }
7912 }
7913
7914 void RequestQuitGame(boolean ask_if_really_quit)
7915 {
7916   if (AllPlayersGone ||
7917       !ask_if_really_quit ||
7918       level_editor_test_game ||
7919       Request("Do you really want to quit the game ?",
7920               REQ_ASK | REQ_STAY_CLOSED))
7921   {
7922 #if defined(PLATFORM_UNIX)
7923     if (options.network)
7924       SendToServer_StopPlaying();
7925     else
7926 #endif
7927     {
7928       game_status = GAME_MODE_MAIN;
7929       DrawMainMenu();
7930     }
7931   }
7932   else
7933   {
7934     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
7935   }
7936 }
7937
7938
7939 /* ---------- new game button stuff ---------------------------------------- */
7940
7941 /* graphic position values for game buttons */
7942 #define GAME_BUTTON_XSIZE       30
7943 #define GAME_BUTTON_YSIZE       30
7944 #define GAME_BUTTON_XPOS        5
7945 #define GAME_BUTTON_YPOS        215
7946 #define SOUND_BUTTON_XPOS       5
7947 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
7948
7949 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
7950 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
7951 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
7952 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
7953 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
7954 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
7955
7956 static struct
7957 {
7958   int x, y;
7959   int gadget_id;
7960   char *infotext;
7961 } gamebutton_info[NUM_GAME_BUTTONS] =
7962 {
7963   {
7964     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
7965     GAME_CTRL_ID_STOP,
7966     "stop game"
7967   },
7968   {
7969     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
7970     GAME_CTRL_ID_PAUSE,
7971     "pause game"
7972   },
7973   {
7974     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
7975     GAME_CTRL_ID_PLAY,
7976     "play game"
7977   },
7978   {
7979     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
7980     SOUND_CTRL_ID_MUSIC,
7981     "background music on/off"
7982   },
7983   {
7984     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
7985     SOUND_CTRL_ID_LOOPS,
7986     "sound loops on/off"
7987   },
7988   {
7989     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
7990     SOUND_CTRL_ID_SIMPLE,
7991     "normal sounds on/off"
7992   }
7993 };
7994
7995 void CreateGameButtons()
7996 {
7997   int i;
7998
7999   for (i=0; i<NUM_GAME_BUTTONS; i++)
8000   {
8001     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
8002     struct GadgetInfo *gi;
8003     int button_type;
8004     boolean checked;
8005     unsigned long event_mask;
8006     int gd_xoffset, gd_yoffset;
8007     int gd_x1, gd_x2, gd_y1, gd_y2;
8008     int id = i;
8009
8010     gd_xoffset = gamebutton_info[i].x;
8011     gd_yoffset = gamebutton_info[i].y;
8012     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
8013     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
8014
8015     if (id == GAME_CTRL_ID_STOP ||
8016         id == GAME_CTRL_ID_PAUSE ||
8017         id == GAME_CTRL_ID_PLAY)
8018     {
8019       button_type = GD_TYPE_NORMAL_BUTTON;
8020       checked = FALSE;
8021       event_mask = GD_EVENT_RELEASED;
8022       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8023       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8024     }
8025     else
8026     {
8027       button_type = GD_TYPE_CHECK_BUTTON;
8028       checked =
8029         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
8030          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
8031          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
8032       event_mask = GD_EVENT_PRESSED;
8033       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
8034       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
8035     }
8036
8037     gi = CreateGadget(GDI_CUSTOM_ID, id,
8038                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
8039                       GDI_X, DX + gd_xoffset,
8040                       GDI_Y, DY + gd_yoffset,
8041                       GDI_WIDTH, GAME_BUTTON_XSIZE,
8042                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
8043                       GDI_TYPE, button_type,
8044                       GDI_STATE, GD_BUTTON_UNPRESSED,
8045                       GDI_CHECKED, checked,
8046                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
8047                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
8048                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
8049                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
8050                       GDI_EVENT_MASK, event_mask,
8051                       GDI_CALLBACK_ACTION, HandleGameButtons,
8052                       GDI_END);
8053
8054     if (gi == NULL)
8055       Error(ERR_EXIT, "cannot create gadget");
8056
8057     game_gadget[id] = gi;
8058   }
8059 }
8060
8061 void FreeGameButtons()
8062 {
8063   int i;
8064
8065   for (i=0; i<NUM_GAME_BUTTONS; i++)
8066     FreeGadget(game_gadget[i]);
8067 }
8068
8069 static void MapGameButtons()
8070 {
8071   int i;
8072
8073   for (i=0; i<NUM_GAME_BUTTONS; i++)
8074     MapGadget(game_gadget[i]);
8075 }
8076
8077 void UnmapGameButtons()
8078 {
8079   int i;
8080
8081   for (i=0; i<NUM_GAME_BUTTONS; i++)
8082     UnmapGadget(game_gadget[i]);
8083 }
8084
8085 static void HandleGameButtons(struct GadgetInfo *gi)
8086 {
8087   int id = gi->custom_id;
8088
8089   if (game_status != GAME_MODE_PLAYING)
8090     return;
8091
8092   switch (id)
8093   {
8094     case GAME_CTRL_ID_STOP:
8095       RequestQuitGame(TRUE);
8096       break;
8097
8098     case GAME_CTRL_ID_PAUSE:
8099       if (options.network)
8100       {
8101 #if defined(PLATFORM_UNIX)
8102         if (tape.pausing)
8103           SendToServer_ContinuePlaying();
8104         else
8105           SendToServer_PausePlaying();
8106 #endif
8107       }
8108       else
8109         TapeTogglePause(TAPE_TOGGLE_MANUAL);
8110       break;
8111
8112     case GAME_CTRL_ID_PLAY:
8113       if (tape.pausing)
8114       {
8115 #if defined(PLATFORM_UNIX)
8116         if (options.network)
8117           SendToServer_ContinuePlaying();
8118         else
8119 #endif
8120         {
8121           tape.pausing = FALSE;
8122           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
8123         }
8124       }
8125       break;
8126
8127     case SOUND_CTRL_ID_MUSIC:
8128       if (setup.sound_music)
8129       { 
8130         setup.sound_music = FALSE;
8131         FadeMusic();
8132       }
8133       else if (audio.music_available)
8134       { 
8135         setup.sound = setup.sound_music = TRUE;
8136
8137         SetAudioMode(setup.sound);
8138         PlayMusic(level_nr);
8139       }
8140       break;
8141
8142     case SOUND_CTRL_ID_LOOPS:
8143       if (setup.sound_loops)
8144         setup.sound_loops = FALSE;
8145       else if (audio.loops_available)
8146       {
8147         setup.sound = setup.sound_loops = TRUE;
8148         SetAudioMode(setup.sound);
8149       }
8150       break;
8151
8152     case SOUND_CTRL_ID_SIMPLE:
8153       if (setup.sound_simple)
8154         setup.sound_simple = FALSE;
8155       else if (audio.sound_available)
8156       {
8157         setup.sound = setup.sound_simple = TRUE;
8158         SetAudioMode(setup.sound);
8159       }
8160       break;
8161
8162     default:
8163       break;
8164   }
8165 }