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