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