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