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