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