rnd-20020519-2-src
[rocksndiamonds.git] / src / game.c
index fc0d64767d09631bef0d1fb55345d7ab2c7b26f2..fc53f25664253a9b30952f60ff8262bffc418041 100644 (file)
@@ -1,31 +1,32 @@
 /***********************************************************
-*  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
+* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-*  (c) 1995-98 Artsoft Entertainment                       *
-*              Holger Schemel                              *
-*              Oststrasse 11a                              *
-*              33604 Bielefeld                             *
-*              phone: ++49 +521 290471                     *
-*              email: aeglos@valinor.owl.de                *
+* (c) 1995-2001 Artsoft Entertainment                      *
+*               Holger Schemel                             *
+*               Detmolder Strasse 189                      *
+*               33604 Bielefeld                            *
+*               Germany                                    *
+*               e-mail: info@artsoft.org                   *
 *----------------------------------------------------------*
-*  game.c                                                  *
+* game.c                                                   *
 ***********************************************************/
 
+#include "libgame/libgame.h"
+
 #include "game.h"
-#include "misc.h"
 #include "tools.h"
 #include "screens.h"
-#include "sound.h"
 #include "init.h"
-#include "buttons.h"
 #include "files.h"
 #include "tape.h"
-#include "joystick.h"
 #include "network.h"
 
 /* this switch controls how rocks move horizontally */
 #define OLD_GAME_BEHAVIOUR     FALSE
 
+/* EXPERIMENTAL STUFF */
+#define USE_NEW_AMOEBA_CODE    FALSE
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 
 /* for Explode() */
 #define EX_PHASE_START         0
-#define EX_NORMAL              0
-#define EX_CENTER              1
-#define EX_BORDER              2
+#define EX_NO_EXPLOSION                0
+#define EX_NORMAL              1
+#define EX_CENTER              2
+#define EX_BORDER              3
 
 /* special positions in the game control window (relative to control window) */
 #define XX_LEVEL               37
 #define DX_TIME                        (DX + XX_TIME)
 #define DY_TIME                        (DY + YY_TIME)
 
-#define IS_LOOP_SOUND(s)       ((s)==SND_KLAPPER || (s)==SND_ROEHR ||  \
-                                (s)==SND_NJAM || (s)==SND_MIEP)
-#define IS_MUSIC_SOUND(s)      ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
-                                (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
-                                (s)==SND_TYGER || (s)==SND_VOYAGER || \
-                                (s)==SND_TWILIGHT)
-
 /* values for player movement speed (which is in fact a delay value) */
 #define MOVE_DELAY_NORMAL_SPEED        8
 #define MOVE_DELAY_HIGH_SPEED  4
 #define NUM_GAME_BUTTONS               6
 
 /* forward declaration for internal use */
+static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
+static void KillHeroUnlessProtected(int, int);
 
 static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
+static boolean is_loop_sound[NUM_SOUND_EFFECTS];
+static boolean is_loop_sound_initialized = FALSE;
+static int loop_sounds[] =
+{
+  SND_BD_MAGIC_WALL_RUNNING,
+  SND_BD_BUTTERFLY_MOVING,
+  SND_BD_FIREFLY_MOVING,
+  SND_SP_SNIKSNAK_MOVING,
+  SND_SP_ELECTRON_MOVING,
+  SND_DYNAMITE_BURNING,
+  SND_BUG_MOVING,
+  SND_SPACESHIP_MOVING,
+  SND_YAMYAM_MOVING,
+  SND_YAMYAM_WAITING,
+  SND_ROBOT_WHEEL_RUNNING,
+  SND_MAGIC_WALL_RUNNING,
+  SND_BALLOON_MOVING,
+  SND_MOLE_MOVING,
+  SND_TIMEGATE_WHEEL_RUNNING,
+  SND_CONVEYOR_BELT_RUNNING,
+  SND_DYNABOMB_BURNING,
+  SND_PACMAN_MOVING,
+  SND_PENGUIN_MOVING,
+  SND_PIG_MOVING,
+  SND_DRAGON_MOVING,
+  SND_DRAGON_BREATHING_FIRE
+};
+
+#define IS_LOOP_SOUND(x)       (is_loop_sound[x])
+
 
 
 #ifdef DEBUG
@@ -165,17 +192,21 @@ static unsigned int getStateCheckSum(int counter)
 
 void GetPlayerConfig()
 {
-  if (sound_status == SOUND_OFF)
+  if (!audio.sound_available)
     setup.sound = FALSE;
 
-  if (!sound_loops_allowed)
-  {
+  if (!audio.loops_available)
     setup.sound_loops = FALSE;
+
+  if (!audio.music_available)
     setup.sound_music = FALSE;
-  }
+
+  if (!video.fullscreen_available)
+    setup.fullscreen = FALSE;
 
   setup.sound_simple = setup.sound;
 
+  SetAudioMode(setup.sound);
   InitJoysticks();
 }
 
@@ -188,19 +219,19 @@ static int getBeltNrFromElement(int element)
 
 static int getBeltNrFromSwitchElement(int element)
 {
-  return (element < EL_BELT2_SWITCH_L ? 0 :
-         element < EL_BELT3_SWITCH_L ? 1 :
-         element < EL_BELT4_SWITCH_L ? 2 : 3);
+  return (element < EL_BELT2_SWITCH_LEFT ? 0 :
+         element < EL_BELT3_SWITCH_LEFT ? 1 :
+         element < EL_BELT4_SWITCH_LEFT ? 2 : 3);
 }
 
 static int getBeltDirNrFromSwitchElement(int element)
 {
   static int belt_base_element[4] =
   {
-    EL_BELT1_SWITCH_L,
-    EL_BELT2_SWITCH_L,
-    EL_BELT3_SWITCH_L,
-    EL_BELT4_SWITCH_L
+    EL_BELT1_SWITCH_LEFT,
+    EL_BELT2_SWITCH_LEFT,
+    EL_BELT3_SWITCH_LEFT,
+    EL_BELT4_SWITCH_LEFT
   };
 
   int belt_nr = getBeltNrFromSwitchElement(element);
@@ -227,8 +258,17 @@ static void InitField(int x, int y, boolean init_game)
 {
   switch (Feld[x][y])
   {
-    case EL_SPIELFIGUR:
     case EL_SP_MURPHY:
+      if (init_game)
+      {
+       if (stored_player[0].present)
+       {
+         Feld[x][y] = EL_SP_MURPHY_CLONE;
+         break;
+       }
+      }
+      /* no break! */
+    case EL_SPIELFIGUR:
       if (init_game)
        Feld[x][y] = EL_SPIELER1;
       /* no break! */
@@ -253,7 +293,7 @@ static void InitField(int x, int y, boolean init_game)
 
          StorePlayer[x][y] = Feld[x][y];
 
-         if (options.verbose)
+         if (options.debug)
          {
            printf("Player %d activated.\n", player->element_nr);
            printf("[Local player is %d and currently %s.]\n",
@@ -281,36 +321,41 @@ static void InitField(int x, int y, boolean init_game)
        Feld[x][y] = EL_BADEWANNE5;
       break;
 
-    case EL_KAEFER_R:
-    case EL_KAEFER_O:
-    case EL_KAEFER_L:
-    case EL_KAEFER_U:
+    case EL_KAEFER_RIGHT:
+    case EL_KAEFER_UP:
+    case EL_KAEFER_LEFT:
+    case EL_KAEFER_DOWN:
     case EL_KAEFER:
-    case EL_FLIEGER_R:
-    case EL_FLIEGER_O:
-    case EL_FLIEGER_L:
-    case EL_FLIEGER_U:
+    case EL_FLIEGER_RIGHT:
+    case EL_FLIEGER_UP:
+    case EL_FLIEGER_LEFT:
+    case EL_FLIEGER_DOWN:
     case EL_FLIEGER:
-    case EL_BUTTERFLY_R:
-    case EL_BUTTERFLY_O:
-    case EL_BUTTERFLY_L:
-    case EL_BUTTERFLY_U:
+    case EL_BUTTERFLY_RIGHT:
+    case EL_BUTTERFLY_UP:
+    case EL_BUTTERFLY_LEFT:
+    case EL_BUTTERFLY_DOWN:
     case EL_BUTTERFLY:
-    case EL_FIREFLY_R:
-    case EL_FIREFLY_O:
-    case EL_FIREFLY_L:
-    case EL_FIREFLY_U:
+    case EL_FIREFLY_RIGHT:
+    case EL_FIREFLY_UP:
+    case EL_FIREFLY_LEFT:
+    case EL_FIREFLY_DOWN:
     case EL_FIREFLY:
-    case EL_PACMAN_R:
-    case EL_PACMAN_O:
-    case EL_PACMAN_L:
-    case EL_PACMAN_U:
+    case EL_PACMAN_RIGHT:
+    case EL_PACMAN_UP:
+    case EL_PACMAN_LEFT:
+    case EL_PACMAN_DOWN:
     case EL_MAMPFER:
     case EL_MAMPFER2:
     case EL_ROBOT:
     case EL_PACMAN:
     case EL_SP_SNIKSNAK:
     case EL_SP_ELECTRON:
+    case EL_MOLE_LEFT:
+    case EL_MOLE_RIGHT:
+    case EL_MOLE_UP:
+    case EL_MOLE_DOWN:
+    case EL_MOLE:
       InitMovDir(x, y);
       break;
 
@@ -339,7 +384,6 @@ static void InitField(int x, int y, boolean init_game)
       local_player->sokobanfields_still_needed++;
       break;
 
-    case EL_MAULWURF:
     case EL_PINGUIN:
       local_player->friends_still_needed++;
       break;
@@ -366,18 +410,18 @@ static void InitField(int x, int y, boolean init_game)
       Feld[x][y] = EL_EM_KEY_4;
       break;
 
-    case EL_BELT1_SWITCH_L:
-    case EL_BELT1_SWITCH_M:
-    case EL_BELT1_SWITCH_R:
-    case EL_BELT2_SWITCH_L:
-    case EL_BELT2_SWITCH_M:
-    case EL_BELT2_SWITCH_R:
-    case EL_BELT3_SWITCH_L:
-    case EL_BELT3_SWITCH_M:
-    case EL_BELT3_SWITCH_R:
-    case EL_BELT4_SWITCH_L:
-    case EL_BELT4_SWITCH_M:
-    case EL_BELT4_SWITCH_R:
+    case EL_BELT1_SWITCH_LEFT:
+    case EL_BELT1_SWITCH_MIDDLE:
+    case EL_BELT1_SWITCH_RIGHT:
+    case EL_BELT2_SWITCH_LEFT:
+    case EL_BELT2_SWITCH_MIDDLE:
+    case EL_BELT2_SWITCH_RIGHT:
+    case EL_BELT3_SWITCH_LEFT:
+    case EL_BELT3_SWITCH_MIDDLE:
+    case EL_BELT3_SWITCH_RIGHT:
+    case EL_BELT4_SWITCH_LEFT:
+    case EL_BELT4_SWITCH_MIDDLE:
+    case EL_BELT4_SWITCH_RIGHT:
       if (init_game)
       {
        int belt_nr = getBeltNrFromSwitchElement(Feld[x][y]);
@@ -403,7 +447,7 @@ static void InitField(int x, int y, boolean init_game)
 
     case EL_LIGHT_SWITCH_ON:
       if (init_game)
-       game.light_time_left = 10 * FRAMES_PER_SECOND;
+       game.light_time_left = level.time_light * FRAMES_PER_SECOND;
       break;
 
     default:
@@ -411,6 +455,26 @@ static void InitField(int x, int y, boolean init_game)
   }
 }
 
+void DrawGameDoorValues()
+{
+  int i, j;
+
+  for (i=0; i<MAX_PLAYERS; i++)
+    for (j=0; j<4; j++)
+      if (stored_player[i].key[j])
+       DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
+                          GFX_SCHLUESSEL1 + j);
+
+  DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
+          int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
+          int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_SCORE, DY + YY_SCORE,
+          int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_TIME, DY + YY_TIME,
+          int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+}
+
 void InitGame()
 {
   int i, j, x, y;
@@ -418,6 +482,14 @@ void InitGame()
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
 
+#if DEBUG
+#if USE_NEW_AMOEBA_CODE
+  printf("Using new amoeba code.\n");
+#else
+  printf("Using old amoeba code.\n");
+#endif
+#endif
+
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
 
@@ -461,20 +533,24 @@ void InitGame()
 
     player->frame_reset_delay = 0;
 
-    player->push_delay = 0;
-    player->push_delay_value = 5;
-
-    player->move_delay = 0;
     player->last_move_dir = MV_NO_MOVING;
+    player->is_moving = FALSE;
 
+    player->move_delay = -1;   /* no initial move delay */
     player->move_delay_value =
       (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
+    player->push_delay = 0;
+    player->push_delay_value = 5;
+
     player->snapped = FALSE;
 
     player->last_jx = player->last_jy = 0;
     player->jx = player->jy = 0;
 
+    player->shield_passive_time_left = 0;
+    player->shield_active_time_left = 0;
+
     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
 
@@ -484,7 +560,7 @@ void InitGame()
 
   network_player_action_received = FALSE;
 
-#ifndef MSDOS
+#if defined(PLATFORM_UNIX)
   /* initial null action */
   if (network_playing)
     SendToServer_MovePlayer(MV_NO_MOVING);
@@ -492,7 +568,6 @@ void InitGame()
 
   ZX = ZY = -1;
 
-  game.yam_content_nr = 0;
   FrameCounter = 0;
   TimeFrames = 0;
   TimePlayed = 0;
@@ -505,10 +580,16 @@ void InitGame()
   ScrollStepSize = 0;  /* will be correctly initialized by ScrollScreen() */
 
   AllPlayersGone = FALSE;
+
+  game.yam_content_nr = 0;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
-  game.switchgate_pos = 0;
   game.light_time_left = 0;
+  game.timegate_time_left = 0;
+  game.switchgate_pos = 0;
+  game.balloon_dir = MV_NO_MOVING;
+  game.explosions_delayed = TRUE;
+
   for (i=0; i<4; i++)
   {
     game.belt_dir[i] = MV_NO_MOVING;
@@ -529,6 +610,7 @@ void InitGame()
       AmoebaNr[x][y] = 0;
       JustStopped[x][y] = 0;
       Stop[x][y] = FALSE;
+      ExplodeField[x][y] = EX_NO_EXPLOSION;
     }
   }
 
@@ -630,7 +712,7 @@ void InitGame()
        tape.player_participates[i] = TRUE;
   }
 
-  if (options.verbose)
+  if (options.debug)
   {
     for (i=0; i<MAX_PLAYERS; i++)
     {
@@ -646,10 +728,63 @@ void InitGame()
     }
   }
 
+  /* initialize sound effect properties */
+  if (!is_loop_sound_initialized)
+  {
+    int i;
+
+    for (i=0; i<NUM_SOUND_EFFECTS; i++)
+      is_loop_sound[i] = FALSE;
+
+    for (i=0; i<SIZEOF_ARRAY_INT(loop_sounds); i++)
+      is_loop_sound[loop_sounds[i]] = TRUE;
+
+    is_loop_sound_initialized = TRUE;
+  }
+
+  game.version = (tape.playing ? tape.game_version : level.game_version);
   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
 
+  /* dynamically adjust element properties according to game engine version */
+  {
+    static int ep_em_slippery_wall[] =
+    {
+      EL_BETON,
+      EL_MAUERWERK,
+      EL_MAUER_LEBT,
+      EL_MAUER_X,
+      EL_MAUER_Y,
+      EL_MAUER_XY
+    };
+#if 1
+    static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
+#else
+    static int ep_em_slippery_wall_num =
+      sizeof(ep_em_slippery_wall) / sizeof(int);
+#endif
+
+    /*
+    printf("level %d: game.version == %06d\n", level_nr, level.game_version);
+    printf("         file_version == %06d\n", level.file_version);
+    */
+
+    for (i=0; i<ep_em_slippery_wall_num; i++)
+    {
+#if 1
+      if (level.em_slippery_gems)      /* special EM style gems behaviour */
+#else
+      if (game.version >= GAME_VERSION_2_0)
+#endif
+       Elementeigenschaften2[ep_em_slippery_wall[i]] |=
+         EP_BIT_EM_SLIPPERY_WALL;
+      else
+       Elementeigenschaften2[ep_em_slippery_wall[i]] &=
+         ~EP_BIT_EM_SLIPPERY_WALL;
+    }
+  }
+
   if (BorderElement == EL_LEERRAUM)
   {
     SBX_Left = 0;
@@ -688,39 +823,36 @@ void InitGame()
   DrawAllPlayers();
   FadeToFront();
 
+  /* after drawing the level, correct some elements */
+  if (game.timegate_time_left == 0)
+    CloseAllOpenTimegates();
+
   if (setup.soft_scrolling)
-    XCopyArea(display, fieldbuffer, backbuffer, gc,
-             FX, FY, SXSIZE, SYSIZE, SX, SY);
+    BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
 
   redraw_mask |= REDRAW_FROM_BACKBUFFER;
 
   /* copy default game door content to main double buffer */
-  XCopyArea(display, pix[PIX_DOOR], drawto, gc,
-           DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+  BlitBitmap(pix[PIX_DOOR], drawto,
+            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
 
   if (level_nr < 100)
     DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
             int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
   else
   {
-    DrawTextExt(drawto, gc, DX + XX_EMERALDS, DY + YY_EMERALDS,
+    DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
                int2str(level_nr, 3), FS_SMALL, FC_SPECIAL3);
-    XCopyArea(display, drawto, drawto, gc,
-             DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
-             FONT5_XSIZE * 3, FONT5_YSIZE - 1,
-             DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
+    BlitBitmap(drawto, drawto,
+              DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
+              FONT5_XSIZE * 3, FONT5_YSIZE - 1,
+              DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
   }
 
-  DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
-          int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
-  DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
-          int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
-  DrawText(DX + XX_SCORE, DY + YY_SCORE,
-          int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
-  DrawText(DX + XX_TIME, DY + YY_TIME,
-          int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+  DrawGameDoorValues();
 
   UnmapGameButtons();
+  UnmapTapeButtons();
   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
@@ -728,17 +860,18 @@ void InitGame()
   MapTapeButtons();
 
   /* copy actual game door content to door double buffer for OpenDoor() */
-  XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
-           DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+  BlitBitmap(drawto, pix[PIX_DB_DOOR],
+            DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
 
   OpenDoor(DOOR_OPEN_ALL);
 
+  PlaySoundStereo(SND_GAME_STARTING, SOUND_MAX_RIGHT);
   if (setup.sound_music)
-    PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
+    PlayMusic(level_nr);
 
-  XAutoRepeatOff(display);
+  KeyboardAutoRepeatOff();
 
-  if (options.verbose)
+  if (options.debug)
   {
     for (i=0; i<4; i++)
       printf("Player %d %sactive.\n",
@@ -756,52 +889,53 @@ void InitMovDir(int x, int y)
     {  0, -1 },
     { -1,  0 }
   };
-  static int direction[2][4] =
+  static int direction[3][4] =
   {
     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
-    { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP }
+    { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
+    { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
   };
 
   switch(element)
   {
-    case EL_KAEFER_R:
-    case EL_KAEFER_O:
-    case EL_KAEFER_L:
-    case EL_KAEFER_U:
+    case EL_KAEFER_RIGHT:
+    case EL_KAEFER_UP:
+    case EL_KAEFER_LEFT:
+    case EL_KAEFER_DOWN:
       Feld[x][y] = EL_KAEFER;
-      MovDir[x][y] = direction[0][element - EL_KAEFER_R];
+      MovDir[x][y] = direction[0][element - EL_KAEFER_RIGHT];
       break;
 
-    case EL_FLIEGER_R:
-    case EL_FLIEGER_O:
-    case EL_FLIEGER_L:
-    case EL_FLIEGER_U:
+    case EL_FLIEGER_RIGHT:
+    case EL_FLIEGER_UP:
+    case EL_FLIEGER_LEFT:
+    case EL_FLIEGER_DOWN:
       Feld[x][y] = EL_FLIEGER;
-      MovDir[x][y] = direction[0][element - EL_FLIEGER_R];
+      MovDir[x][y] = direction[0][element - EL_FLIEGER_RIGHT];
       break;
 
-    case EL_BUTTERFLY_R:
-    case EL_BUTTERFLY_O:
-    case EL_BUTTERFLY_L:
-    case EL_BUTTERFLY_U:
+    case EL_BUTTERFLY_RIGHT:
+    case EL_BUTTERFLY_UP:
+    case EL_BUTTERFLY_LEFT:
+    case EL_BUTTERFLY_DOWN:
       Feld[x][y] = EL_BUTTERFLY;
-      MovDir[x][y] = direction[0][element - EL_BUTTERFLY_R];
+      MovDir[x][y] = direction[0][element - EL_BUTTERFLY_RIGHT];
       break;
 
-    case EL_FIREFLY_R:
-    case EL_FIREFLY_O:
-    case EL_FIREFLY_L:
-    case EL_FIREFLY_U:
+    case EL_FIREFLY_RIGHT:
+    case EL_FIREFLY_UP:
+    case EL_FIREFLY_LEFT:
+    case EL_FIREFLY_DOWN:
       Feld[x][y] = EL_FIREFLY;
-      MovDir[x][y] = direction[0][element - EL_FIREFLY_R];
+      MovDir[x][y] = direction[0][element - EL_FIREFLY_RIGHT];
       break;
 
-    case EL_PACMAN_R:
-    case EL_PACMAN_O:
-    case EL_PACMAN_L:
-    case EL_PACMAN_U:
+    case EL_PACMAN_RIGHT:
+    case EL_PACMAN_UP:
+    case EL_PACMAN_LEFT:
+    case EL_PACMAN_DOWN:
       Feld[x][y] = EL_PACMAN;
-      MovDir[x][y] = direction[0][element - EL_PACMAN_R];
+      MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
       break;
 
     case EL_SP_SNIKSNAK:
@@ -812,6 +946,14 @@ void InitMovDir(int x, int y)
       MovDir[x][y] = MV_LEFT;
       break;
 
+    case EL_MOLE_LEFT:
+    case EL_MOLE_RIGHT:
+    case EL_MOLE_UP:
+    case EL_MOLE_DOWN:
+      Feld[x][y] = EL_MOLE;
+      MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
+      break;
+
     default:
       MovDir[x][y] = 1 << RND(4);
       if (element != EL_KAEFER &&
@@ -869,22 +1011,25 @@ void InitAmoebaNr(int x, int y)
 void GameWon()
 {
   int hi_pos;
-  int bumplevel = FALSE;
+  boolean raise_level = FALSE;
 
   if (local_player->MovPos)
     return;
 
   local_player->LevelSolved = FALSE;
 
+  PlaySoundStereo(SND_GAME_WINNING, SOUND_MAX_RIGHT);
+
   if (TimeLeft)
   {
-    if (setup.sound_loops)
-      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+    if (!tape.playing && setup.sound_loops)
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+                  SND_CTRL_PLAY_LOOP);
 
-    while(TimeLeft > 0)
+    while (TimeLeft > 0)
     {
-      if (!setup.sound_loops)
-       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
+      if (!tape.playing && !setup.sound_loops)
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
       if (TimeLeft > 0 && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
@@ -893,21 +1038,24 @@ void GameWon()
        TimeLeft--;
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       BackToFront();
-      Delay(10);
+
+      if (!tape.playing)
+       Delay(10);
     }
 
-    if (setup.sound_loops)
-      StopSound(SND_SIRR);
+    if (!tape.playing && setup.sound_loops)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
   }
   else if (level.time == 0)            /* level without time limit */
   {
-    if (setup.sound_loops)
-      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+    if (!tape.playing && setup.sound_loops)
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+                  SND_CTRL_PLAY_LOOP);
 
-    while(TimePlayed < 999)
+    while (TimePlayed < 999)
     {
-      if (!setup.sound_loops)
-       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
+      if (!tape.playing && !setup.sound_loops)
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
       if (TimePlayed < 999 && !(TimePlayed % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimePlayed < 900 && !(TimePlayed % 10))
@@ -916,14 +1064,18 @@ void GameWon()
        TimePlayed++;
       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
       BackToFront();
-      Delay(10);
+
+      if (!tape.playing)
+       Delay(10);
     }
 
-    if (setup.sound_loops)
-      StopSound(SND_SIRR);
+    if (!tape.playing && setup.sound_loops)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
   }
 
+#if 0
   FadeSounds();
+#endif
 
   /* Hero disappears */
   DrawLevelField(ExitX, ExitY);
@@ -940,18 +1092,35 @@ void GameWon()
     SaveTape(tape.level_nr);           /* Ask to save tape */
   }
 
+  if (level_nr == leveldir_current->handicap_level)
+  {
+    leveldir_current->handicap_level++;
+    SaveLevelSetup_SeriesInfo();
+  }
+
+  if (level_editor_test_game)
+    local_player->score = -1;  /* no highscore when playing from editor */
+  else if (level_nr < leveldir_current->last_level)
+    raise_level = TRUE;                /* advance to next level */
+
   if ((hi_pos = NewHiScore()) >= 0) 
   {
     game_status = HALLOFFAME;
     DrawHallOfFame(hi_pos);
-    if (bumplevel && TAPE_IS_EMPTY(tape))
+    if (raise_level)
+    {
       level_nr++;
+      TapeErase();
+    }
   }
   else
   {
     game_status = MAINMENU;
-    if (bumplevel && TAPE_IS_EMPTY(tape))
+    if (raise_level)
+    {
       level_nr++;
+      TapeErase();
+    }
     DrawMainMenu();
   }
 
@@ -1124,17 +1293,16 @@ void RemoveMovingField(int x, int y)
   }
 
   if (Feld[x][y] == EL_BLOCKED &&
-      (Store[oldx][oldy] == EL_MORAST_LEER ||
-       Store[oldx][oldy] == EL_SIEB_LEER ||
-       Store[oldx][oldy] == EL_SIEB2_LEER ||
-       Store[oldx][oldy] == EL_AMOEBE_NASS))
-  {
-    Feld[oldx][oldy] = Store[oldx][oldy];
-    Store[oldx][oldy] = Store2[oldx][oldy] = 0;
-  }
+      (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
+       Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
+       Feld[oldx][oldy] == EL_MAGIC_WALL_BD_EMPTYING ||
+       Feld[oldx][oldy] == EL_AMOEBA_DRIPPING))
+    Feld[oldx][oldy] = get_next_element(Feld[oldx][oldy]);
   else
     Feld[oldx][oldy] = EL_LEERRAUM;
 
+  Store[oldx][oldy] = Store2[oldx][oldy] = 0;
+
   Feld[newx][newy] = EL_LEERRAUM;
   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
@@ -1182,7 +1350,12 @@ void CheckDynamite(int x, int y)
     if (MovDelay[x][y])
     {
       if (!(MovDelay[x][y] % 12))
-       PlaySoundLevel(x, y, SND_ZISCH);
+      {
+       if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
+         PlaySoundLevel(x, y, SND_DYNAMITE_BURNING);
+       else
+         PlaySoundLevel(x, y, SND_DYNABOMB_BURNING);
+      }
 
       if (IS_ACTIVE_BOMB(Feld[x][y]))
       {
@@ -1196,18 +1369,28 @@ void CheckDynamite(int x, int y)
     }
   }
 
-  StopSound(SND_ZISCH);
+  if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
+    StopSound(SND_DYNAMITE_BURNING);
+  else
+    StopSound(SND_DYNABOMB_BURNING);
+
   Bang(x, y);
 }
 
 void Explode(int ex, int ey, int phase, int mode)
 {
   int x, y;
-  int num_phase = 9, delay = 2;
+  int num_phase = 9, delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
   int last_phase = num_phase * delay;
   int half_phase = (num_phase / 2) * delay;
   int first_phase_after_start = EX_PHASE_START + 1;
 
+  if (game.explosions_delayed)
+  {
+    ExplodeField[ex][ey] = mode;
+    return;
+  }
+
   if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
     int center_element = Feld[ex][ey];
@@ -1240,10 +1423,22 @@ void Explode(int ex, int ey, int phase, int mode)
       if (IS_MASSIVE(element) || element == EL_BURNING)
        continue;
 
+      if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
+      {
+       if (IS_ACTIVE_BOMB(element))
+       {
+         /* re-activate things under the bomb like gate or penguin */
+         Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_LEERRAUM);
+         Store[x][y] = 0;
+       }
+
+       continue;
+      }
+
       if (element == EL_EXPLODING)
        element = Store2[x][y];
 
-      if (IS_PLAYER(ex, ey))
+      if (IS_PLAYER(ex, ey) && !PLAYER_PROTECTED(ex, ey))
       {
        switch(StorePlayer[ex][ey])
        {
@@ -1265,7 +1460,7 @@ void Explode(int ex, int ey, int phase, int mode)
        if (game.emulation == EMU_SUPAPLEX)
          Store[x][y] = EL_LEERRAUM;
       }
-      else if (center_element == EL_MAULWURF)
+      else if (center_element == EL_MOLE)
        Store[x][y] = EL_EDELSTEIN_ROT;
       else if (center_element == EL_PINGUIN)
        Store[x][y] = EL_EDELSTEIN_LILA;
@@ -1291,6 +1486,10 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_EDELSTEIN_ROT;
       else if (element == EL_ERZ_EDEL_LILA)
        Store[x][y] = EL_EDELSTEIN_LILA;
+      else if (element == EL_WALL_PEARL)
+       Store[x][y] = EL_PEARL;
+      else if (element == EL_WALL_CRYSTAL)
+       Store[x][y] = EL_CRYSTAL;
       else if (!IS_PFORTE(Store[x][y]))
        Store[x][y] = EL_LEERRAUM;
 
@@ -1344,7 +1543,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int element = Store2[x][y];
 
     if (IS_PLAYER(x, y))
-      KillHero(PLAYERINFO(x, y));
+      KillHeroUnlessProtected(x, y);
     else if (IS_EXPLOSIVE(element))
     {
       Feld[x][y] = Store2[x][y];
@@ -1366,6 +1565,9 @@ void Explode(int ex, int ey, int phase, int mode)
     if (CAN_MOVE(element) || COULD_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
+
+    if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
+      StorePlayer[x][y] = 0;
   }
   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
@@ -1379,7 +1581,15 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
 
-    DrawGraphic(SCREENX(x), SCREENY(y), graphic + (phase / delay - 1));
+    graphic += (phase / delay - 1);
+
+    if (IS_PFORTE(Store[x][y]))
+    {
+      DrawLevelElement(x, y, Store[x][y]);
+      DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic);
+    }
+    else
+      DrawGraphic(SCREENX(x), SCREENY(y), graphic);
   }
 }
 
@@ -1440,12 +1650,14 @@ void Bang(int x, int y)
   int element = Feld[x][y];
 
   if (game.emulation == EMU_SUPAPLEX)
-    PlaySoundLevel(x, y, SND_SP_BOOOM);
+    PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
   else
-    PlaySoundLevel(x, y, SND_ROAAAR);
+    PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
 
+#if 0
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
     element = EL_LEERRAUM;
+#endif
 
   switch(element)
   {
@@ -1457,6 +1669,7 @@ void Bang(int x, int y)
     case EL_MAMPFER2:
     case EL_ROBOT:
     case EL_PACMAN:
+    case EL_MOLE:
       RaiseScoreElement(element);
       Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
@@ -1469,11 +1682,13 @@ void Bang(int x, int y)
     case EL_DYNABOMB_XL:
       DynaExplode(x, y);
       break;
-    case EL_MAULWURF:
     case EL_PINGUIN:
     case EL_BIRNE_AUS:
     case EL_BIRNE_EIN:
-      Explode(x, y, EX_PHASE_START, EX_CENTER);
+      if (IS_PLAYER(x, y))
+       Explode(x, y, EX_PHASE_START, EX_NORMAL);
+      else
+       Explode(x, y, EX_PHASE_START, EX_CENTER);
       break;
     default:
       Explode(x, y, EX_PHASE_START, EX_NORMAL);
@@ -1487,7 +1702,7 @@ void Blurb(int x, int y)
 
   if (element != EL_BLURB_LEFT && element != EL_BLURB_RIGHT)   /* start */
   {
-    PlaySoundLevel(x, y, SND_BLURB);
+    PlaySoundLevel(x, y, SND_ACID_SPLASHING);
     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
        (!IN_LEV_FIELD(x-1, y-1) ||
         !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
@@ -1527,10 +1742,10 @@ static void ToggleBeltSwitch(int x, int y)
 {
   static int belt_base_element[4] =
   {
-    EL_BELT1_SWITCH_L,
-    EL_BELT2_SWITCH_L,
-    EL_BELT3_SWITCH_L,
-    EL_BELT4_SWITCH_L
+    EL_BELT1_SWITCH_LEFT,
+    EL_BELT2_SWITCH_LEFT,
+    EL_BELT3_SWITCH_LEFT,
+    EL_BELT4_SWITCH_LEFT
   };
   static int belt_move_dir[4] =
   {
@@ -1604,51 +1819,91 @@ static void ToggleSwitchgateSwitch(int x, int y)
               element == EL_SWITCHGATE_OPENING)
       {
        Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
-       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+       PlaySoundLevel(xx, yy, SND_SWITCHGATE_CLOSING);
       }
       else if (element == EL_SWITCHGATE_CLOSED ||
               element == EL_SWITCHGATE_CLOSING)
       {
        Feld[xx][yy] = EL_SWITCHGATE_OPENING;
-       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+       PlaySoundLevel(xx, yy, SND_SWITCHGATE_OPENING);
       }
     }
   }
 }
 
-static void ToggleLightSwitch(int x, int y)
+static void RedrawAllLightSwitchesAndInvisibleElements()
 {
-  int element = Feld[x][y];
-  int xx, yy;
-
-  game.light_time_left =
-    (element == EL_LIGHT_SWITCH_OFF ? 10 * FRAMES_PER_SECOND : 0);
+  int x, y;
 
-  for (yy=0; yy<lev_fieldy; yy++)
+  for (y=0; y<lev_fieldy; y++)
   {
-    for (xx=0; xx<lev_fieldx; xx++)
+    for (x=0; x<lev_fieldx; x++)
     {
-      int element = Feld[xx][yy];
+      int element = Feld[x][y];
 
       if (element == EL_LIGHT_SWITCH_OFF &&
          game.light_time_left > 0)
       {
-       Feld[xx][yy] = EL_LIGHT_SWITCH_ON;
-       DrawLevelField(xx, yy);
+       Feld[x][y] = EL_LIGHT_SWITCH_ON;
+       DrawLevelField(xy);
       }
       else if (element == EL_LIGHT_SWITCH_ON &&
               game.light_time_left == 0)
       {
-       Feld[xx][yy] = EL_LIGHT_SWITCH_OFF;
-       DrawLevelField(xx, yy);
+       Feld[x][y] = EL_LIGHT_SWITCH_OFF;
+       DrawLevelField(xy);
       }
 
       if (element == EL_INVISIBLE_STEEL ||
          element == EL_UNSICHTBAR ||
          element == EL_SAND_INVISIBLE)
+       DrawLevelField(x, y);
+    }
+  }
+}
+
+static void ToggleLightSwitch(int x, int y)
+{
+  int element = Feld[x][y];
+
+  game.light_time_left =
+    (element == EL_LIGHT_SWITCH_OFF ?
+     level.time_light * FRAMES_PER_SECOND : 0);
+
+  RedrawAllLightSwitchesAndInvisibleElements();
+}
+
+static void ActivateTimegateSwitch(int x, int y)
+{
+  int xx, yy;
+
+  game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
+
+  for (yy=0; yy<lev_fieldy; yy++)
+  {
+    for (xx=0; xx<lev_fieldx; xx++)
+    {
+      int element = Feld[xx][yy];
+
+      if (element == EL_TIMEGATE_CLOSED ||
+         element == EL_TIMEGATE_CLOSING)
+      {
+       Feld[xx][yy] = EL_TIMEGATE_OPENING;
+       PlaySoundLevel(xx, yy, SND_TIMEGATE_OPENING);
+      }
+
+      /*
+      else if (element == EL_TIMEGATE_SWITCH_ON)
+      {
+       Feld[xx][yy] = EL_TIMEGATE_SWITCH_OFF;
        DrawLevelField(xx, yy);
+      }
+      */
+
     }
   }
+
+  Feld[x][y] = EL_TIMEGATE_SWITCH_ON;
 }
 
 void Impact(int x, int y)
@@ -1676,18 +1931,26 @@ void Impact(int x, int y)
     return;
   }
 
-  if ((element == EL_BOMBE || element == EL_SP_DISK_ORANGE) &&
+  if ((element == EL_BOMBE ||
+       element == EL_SP_DISK_ORANGE ||
+       element == EL_DX_SUPABOMB) &&
       (lastline || object_hit))        /* element is bomb */
   {
     Bang(x, y);
     return;
   }
+  else if (element == EL_PEARL)
+  {
+    Feld[x][y] = EL_PEARL_BREAKING;
+    PlaySoundLevel(x, y, SND_PEARL_BREAKING);
+    return;
+  }
 
   if (element == EL_TROPFEN && (lastline || object_hit))       /* acid drop */
   {
     if (object_hit && IS_PLAYER(x, y+1))
-      KillHero(PLAYERINFO(x, y+1));
-    else if (object_hit && (smashed == EL_MAULWURF || smashed == EL_PINGUIN))
+      KillHeroUnlessProtected(x, y+1);
+    else if (object_hit && smashed == EL_PINGUIN)
       Bang(x, y+1);
     else
     {
@@ -1700,29 +1963,33 @@ void Impact(int x, int y)
   if (!lastline && object_hit)         /* check which object was hit */
   {
     if (CAN_CHANGE(element) && 
-       (smashed == EL_SIEB_INAKTIV || smashed == EL_SIEB2_INAKTIV))
+       (smashed == EL_MAGIC_WALL_OFF || smashed == EL_MAGIC_WALL_BD_OFF))
     {
-      int xy;
+      int xx, yy;
       int activated_magic_wall =
-       (smashed == EL_SIEB_INAKTIV ? EL_SIEB_LEER : EL_SIEB2_LEER);
+       (smashed == EL_MAGIC_WALL_OFF ? EL_MAGIC_WALL_EMPTY :
+        EL_MAGIC_WALL_BD_EMPTY);
 
       /* activate magic wall / mill */
-
-      for (y=0; y<lev_fieldy; y++)
-       for (x=0; x<lev_fieldx; x++)
-         if (Feld[x][y] == smashed)
-           Feld[x][y] = activated_magic_wall;
+      for (yy=0; yy<lev_fieldy; yy++)
+       for (xx=0; xx<lev_fieldx; xx++)
+         if (Feld[xx][yy] == smashed)
+           Feld[xx][yy] = activated_magic_wall;
 
       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
       game.magic_wall_active = TRUE;
+
+      PlaySoundLevel(x, y, (smashed == EL_MAGIC_WALL_OFF ?
+                           SND_MAGIC_WALL_ACTIVATING :
+                           SND_BD_MAGIC_WALL_ACTIVATING));
     }
 
     if (IS_PLAYER(x, y+1))
     {
-      KillHero(PLAYERINFO(x, y+1));
+      KillHeroUnlessProtected(x, y+1);
       return;
     }
-    else if (smashed == EL_MAULWURF || smashed == EL_PINGUIN)
+    else if (smashed == EL_PINGUIN)
     {
       Bang(x, y+1);
       return;
@@ -1735,11 +2002,22 @@ void Impact(int x, int y)
        return;
       }
     }
-    else if (element == EL_FELSBROCKEN || element == EL_SP_ZONK)
+    else if ((element == EL_SP_INFOTRON || element == EL_SP_ZONK) &&
+            (smashed == EL_SP_SNIKSNAK || smashed == EL_SP_ELECTRON ||
+             smashed == EL_SP_DISK_ORANGE))
+    {
+      Bang(x, y+1);
+      return;
+    }
+    else if (element == EL_FELSBROCKEN ||
+            element == EL_SP_ZONK ||
+            element == EL_BD_ROCK)
     {
       if (IS_ENEMY(smashed) ||
          smashed == EL_BOMBE || smashed == EL_SP_DISK_ORANGE ||
-         smashed == EL_SONDE || smashed == EL_SCHWEIN || smashed == EL_DRACHE)
+         smashed == EL_DX_SUPABOMB ||
+         smashed == EL_SONDE || smashed == EL_SCHWEIN ||
+         smashed == EL_DRACHE || smashed == EL_MOLE)
       {
        Bang(x, y+1);
        return;
@@ -1754,14 +2032,20 @@ void Impact(int x, int y)
        else if (smashed == EL_KOKOSNUSS)
        {
          Feld[x][y+1] = EL_CRACKINGNUT;
-         PlaySoundLevel(x, y, SND_KNACK);
+         PlaySoundLevel(x, y, SND_NUT_CRACKING);
          RaiseScoreElement(EL_KOKOSNUSS);
          return;
        }
+       else if (smashed == EL_PEARL)
+       {
+         Feld[x][y+1] = EL_PEARL_BREAKING;
+         PlaySoundLevel(x, y, SND_PEARL_BREAKING);
+         return;
+       }
        else if (smashed == EL_DIAMANT)
        {
          Feld[x][y+1] = EL_LEERRAUM;
-         PlaySoundLevel(x, y, SND_QUIRK);
+         PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
          return;
        }
        else if (IS_BELT_SWITCH(smashed))
@@ -1784,9 +2068,14 @@ void Impact(int x, int y)
 
   /* play sound of magic wall / mill */
   if (!lastline &&
-      (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
+      (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ||
+       Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY))
   {
-    PlaySoundLevel(x, y, SND_QUIRK);
+    if (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY)
+      PlaySoundLevel(x, y, SND_MAGIC_WALL_CHANGING);
+    else if (Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY)
+      PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_CHANGING);
+
     return;
   }
 
@@ -1795,40 +2084,49 @@ void Impact(int x, int y)
   {
     int sound;
 
-    switch(element)
+    switch (element)
     {
-      case EL_EDELSTEIN:
       case EL_EDELSTEIN_BD:
+        sound = SND_BD_DIAMOND_IMPACT;
+       break;
+      case EL_EDELSTEIN:
       case EL_EDELSTEIN_GELB:
       case EL_EDELSTEIN_ROT:
       case EL_EDELSTEIN_LILA:
+        sound = SND_EMERALD_IMPACT;
+       break;
       case EL_DIAMANT:
+        sound = SND_DIAMOND_IMPACT;
+       break;
+      case EL_PEARL:
+        sound = SND_PEARL_IMPACT;
+       break;
+      case EL_CRYSTAL:
+        sound = SND_CRYSTAL_IMPACT;
+       break;
       case EL_SP_INFOTRON:
-        sound = SND_PLING;
+        sound = SND_SP_INFOTRON_IMPACT;
        break;
       case EL_KOKOSNUSS:
-       sound = SND_KLUMPF;
+       sound = SND_NUT_IMPACT;
+       break;
+      case EL_BD_ROCK:
+       sound = SND_BD_ROCK_IMPACT;
        break;
       case EL_FELSBROCKEN:
-       sound = SND_KLOPF;
+       sound = SND_ROCK_IMPACT;
        break;
       case EL_SP_ZONK:
-       sound = SND_SP_ZONKDOWN;
-       break;
-      case EL_SCHLUESSEL:
-      case EL_SCHLUESSEL1:
-      case EL_SCHLUESSEL2:
-      case EL_SCHLUESSEL3:
-      case EL_SCHLUESSEL4:
-      case EL_EM_KEY_1:
-      case EL_EM_KEY_2:
-      case EL_EM_KEY_3:
-      case EL_EM_KEY_4:
-       sound = SND_KINK;
+       sound = SND_SP_ZONK_IMPACT;
        break;
       case EL_ZEIT_VOLL:
+       sound = SND_TIME_ORB_FULL_IMPACT;
+       break;
       case EL_ZEIT_LEER:
-       sound = SND_DENG;
+       sound = SND_TIME_ORB_EMPTY_IMPACT;
+       break;
+      case EL_SPRING:
+       sound = SND_SPRING_IMPACT;
        break;
       default:
        sound = -1;
@@ -1886,13 +2184,13 @@ void TurnRound(int x, int y)
 
   if (element == EL_KAEFER || element == EL_BUTTERFLY)
   {
-    TestIfBadThingHitsOtherBadThing(x, y);
+    TestIfBadThingTouchesOtherBadThing(x, y);
 
     if (IN_LEV_FIELD(right_x, right_y) &&
-       IS_FREE_OR_PLAYER(right_x, right_y))
+       IS_FREE(right_x, right_y))
       MovDir[x][y] = right_dir;
     else if (!IN_LEV_FIELD(move_x, move_y) ||
-            !IS_FREE_OR_PLAYER(move_x, move_y))
+            !IS_FREE(move_x, move_y))
       MovDir[x][y] = left_dir;
 
     if (element == EL_KAEFER && MovDir[x][y] != old_move_dir)
@@ -1903,13 +2201,13 @@ void TurnRound(int x, int y)
   else if (element == EL_FLIEGER || element == EL_FIREFLY ||
           element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
   {
-    TestIfBadThingHitsOtherBadThing(x, y);
+    TestIfBadThingTouchesOtherBadThing(x, y);
 
     if (IN_LEV_FIELD(left_x, left_y) &&
-       IS_FREE_OR_PLAYER(left_x, left_y))
+       IS_FREE(left_x, left_y))
       MovDir[x][y] = left_dir;
     else if (!IN_LEV_FIELD(move_x, move_y) ||
-            !IS_FREE_OR_PLAYER(move_x, move_y))
+            !IS_FREE(move_x, move_y))
       MovDir[x][y] = right_dir;
 
     if ((element == EL_FLIEGER ||
@@ -2092,8 +2390,51 @@ void TurnRound(int x, int y)
 
     MovDelay[x][y] = 0;
   }
-  else if (element == EL_ROBOT || element == EL_SONDE ||
-          element == EL_MAULWURF || element == EL_PINGUIN)
+  else if (element == EL_MOLE)
+  {
+    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
+
+    if (IN_LEV_FIELD(move_x, move_y) &&
+       (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) ||
+        Feld[move_x][move_y] == EL_DEAMOEBING))
+      can_move_on = TRUE;
+
+    if (!can_move_on)
+    {
+      if (IN_LEV_FIELD(left_x, left_y) &&
+         (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y])))
+       can_turn_left = TRUE;
+      if (IN_LEV_FIELD(right_x, right_y) &&
+         (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y])))
+       can_turn_right = TRUE;
+
+      if (can_turn_left && can_turn_right)
+       MovDir[x][y] = (RND(2) ? left_dir : right_dir);
+      else if (can_turn_left)
+       MovDir[x][y] = left_dir;
+      else
+       MovDir[x][y] = right_dir;
+    }
+
+    if (MovDir[x][y] != old_move_dir)
+      MovDelay[x][y] = 9;
+  }
+  else if (element == EL_BALLOON)
+  {
+    MovDir[x][y] = game.balloon_dir;
+    MovDelay[x][y] = 0;
+  }
+  else if (element == EL_SPRING_MOVING)
+  {
+    if (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) ||
+       (IN_LEV_FIELD(x, y+1) && IS_FREE(x, y+1)))
+    {
+      Feld[x][y] = EL_SPRING;
+      MovDir[x][y] = MV_NO_MOVING;
+    }
+    MovDelay[x][y] = 0;
+  }
+  else if (element == EL_ROBOT || element == EL_SONDE || element == EL_PINGUIN)
   {
     int attr_x = -1, attr_y = -1;
 
@@ -2128,7 +2469,7 @@ void TurnRound(int x, int y)
       attr_y = ZY;
     }
 
-    if (element == EL_MAULWURF || element == EL_PINGUIN)
+    if (element == EL_PINGUIN)
     {
       int i;
       static int xy[4][2] =
@@ -2194,7 +2535,7 @@ void TurnRound(int x, int y)
        if (IN_LEV_FIELD(newx, newy) &&
            (IS_FREE(newx, newy) ||
             Feld[newx][newy] == EL_SALZSAEURE ||
-            ((element == EL_MAULWURF || element == EL_PINGUIN) &&
+            (element == EL_PINGUIN &&
              (Feld[newx][newy] == EL_AUSGANG_AUF ||
               IS_MAMPF3(Feld[newx][newy])))))
          return;
@@ -2206,7 +2547,7 @@ void TurnRound(int x, int y)
        if (IN_LEV_FIELD(newx, newy) &&
            (IS_FREE(newx, newy) ||
             Feld[newx][newy] == EL_SALZSAEURE ||
-            ((element == EL_MAULWURF || element == EL_PINGUIN) &&
+            (element == EL_PINGUIN &&
              (Feld[newx][newy] == EL_AUSGANG_AUF ||
               IS_MAMPF3(Feld[newx][newy])))))
          return;
@@ -2257,8 +2598,9 @@ void StartMoving(int x, int y)
       if (IS_FREE(x, y+1))
       {
        InitMovingField(x, y, MV_DOWN);
-       Feld[x][y] = EL_FELSBROCKEN;
-       Store[x][y] = EL_MORAST_LEER;
+       Feld[x][y] = EL_QUICKSAND_EMPTYING;
+       Store[x][y] = EL_FELSBROCKEN;
+       PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
       }
       else if (Feld[x][y+1] == EL_MORAST_LEER)
       {
@@ -2274,22 +2616,28 @@ void StartMoving(int x, int y)
 
        Feld[x][y] = EL_MORAST_LEER;
        Feld[x][y+1] = EL_MORAST_VOLL;
+       Store[x][y+1] = Store[x][y];
+       Store[x][y] = 0;
+       PlaySoundLevel(x, y, SND_QUICKSAND_SLIPPING_THROUGH);
       }
     }
-    else if (element == EL_FELSBROCKEN && Feld[x][y+1] == EL_MORAST_LEER)
+    else if ((element == EL_FELSBROCKEN || element == EL_BD_ROCK) &&
+            Feld[x][y+1] == EL_MORAST_LEER)
     {
       InitMovingField(x, y, MV_DOWN);
-      Store[x][y] = EL_MORAST_VOLL;
+      Feld[x][y] = EL_QUICKSAND_FILLING;
+      Store[x][y] = element;
+      PlaySoundLevel(x, y, SND_QUICKSAND_FILLING);
     }
-    else if (element == EL_SIEB_VOLL)
+    else if (element == EL_MAGIC_WALL_FULL)
     {
       if (IS_FREE(x, y+1))
       {
        InitMovingField(x, y, MV_DOWN);
-       Feld[x][y] = EL_CHANGED(Store2[x][y]);
-       Store[x][y] = EL_SIEB_LEER;
+       Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
+       Store[x][y] = EL_CHANGED(Store[x][y]);
       }
-      else if (Feld[x][y+1] == EL_SIEB_LEER)
+      else if (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY/4 + 1;
@@ -2301,21 +2649,21 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_SIEB_LEER;
-       Feld[x][y+1] = EL_SIEB_VOLL;
-       Store2[x][y+1] = EL_CHANGED(Store2[x][y]);
-       Store2[x][y] = 0;
+       Feld[x][y] = EL_MAGIC_WALL_EMPTY;
+       Feld[x][y+1] = EL_MAGIC_WALL_FULL;
+       Store[x][y+1] = EL_CHANGED(Store[x][y]);
+       Store[x][y] = 0;
       }
     }
-    else if (element == EL_SIEB2_VOLL)
+    else if (element == EL_MAGIC_WALL_BD_FULL)
     {
       if (IS_FREE(x, y+1))
       {
        InitMovingField(x, y, MV_DOWN);
-       Feld[x][y] = EL_CHANGED2(Store2[x][y]);
-       Store[x][y] = EL_SIEB2_LEER;
+       Feld[x][y] = EL_MAGIC_WALL_BD_EMPTYING;
+       Store[x][y] = EL_CHANGED2(Store[x][y]);
       }
-      else if (Feld[x][y+1] == EL_SIEB2_LEER)
+      else if (Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY/4 + 1;
@@ -2327,19 +2675,21 @@ void StartMoving(int x, int y)
            return;
        }
 
-       Feld[x][y] = EL_SIEB2_LEER;
-       Feld[x][y+1] = EL_SIEB2_VOLL;
-       Store2[x][y+1] = EL_CHANGED2(Store2[x][y]);
-       Store2[x][y] = 0;
+       Feld[x][y] = EL_MAGIC_WALL_BD_EMPTY;
+       Feld[x][y+1] = EL_MAGIC_WALL_BD_FULL;
+       Store[x][y+1] = EL_CHANGED2(Store[x][y]);
+       Store[x][y] = 0;
       }
     }
     else if (CAN_CHANGE(element) &&
-            (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
+            (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ||
+             Feld[x][y+1] == EL_MAGIC_WALL_BD_EMPTY))
     {
       InitMovingField(x, y, MV_DOWN);
-      Store[x][y] =
-       (Feld[x][y+1] == EL_SIEB_LEER ? EL_SIEB_VOLL : EL_SIEB2_VOLL);
-      Store2[x][y+1] = element;
+      Feld[x][y] =
+       (Feld[x][y+1] == EL_MAGIC_WALL_EMPTY ? EL_MAGIC_WALL_FILLING :
+        EL_MAGIC_WALL_BD_FILLING);
+      Store[x][y] = element;
     }
     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_SALZSAEURE)
     {
@@ -2361,11 +2711,22 @@ void StartMoving(int x, int y)
       Feld[x][y] = EL_AMOEBING;
       Store[x][y] = EL_AMOEBE_NASS;
     }
+    /* Store[x][y+1] must be zero, because:
+       (EL_MORAST_VOLL -> EL_FELSBROCKEN): Store[x][y+1] == EL_MORAST_LEER
+    */
+#if 0
 #if OLD_GAME_BEHAVIOUR
     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
 #else
     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
-            !IS_FALLING(x, y+1) && !JustStopped[x][y+1])
+            !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
+            element != EL_DX_SUPABOMB)
+#endif
+#else
+    else if ((IS_SLIPPERY(Feld[x][y+1]) ||
+             (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
+            !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
+            element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
 #endif
     {
       boolean left  = (x>0 && IS_FREE(x-1, y) &&
@@ -2375,7 +2736,9 @@ void StartMoving(int x, int y)
 
       if (left || right)
       {
-       if (left && right && game.emulation != EMU_BOULDERDASH)
+       if (left && right &&
+           (game.emulation != EMU_BOULDERDASH &&
+            element != EL_BD_ROCK && element != EL_EDELSTEIN_BD))
          left = !(right = RND(2));
 
        InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
@@ -2397,7 +2760,9 @@ void StartMoving(int x, int y)
   {
     int newx, newy;
 
-    if (element == EL_SONDE && JustBeingPushed(x, y))
+    if ((element == EL_SONDE || element == EL_BALLOON ||
+        element == EL_SPRING_MOVING)
+       && JustBeingPushed(x, y))
       return;
 
     if (!MovDelay[x][y])       /* start new movement phase */
@@ -2408,9 +2773,11 @@ void StartMoving(int x, int y)
       if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
       {
        TurnRound(x, y);
-       if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER ||
+       if (MovDelay[x][y] && (element == EL_KAEFER ||
+                              element == EL_FLIEGER ||
                               element == EL_SP_SNIKSNAK ||
-                              element == EL_SP_ELECTRON))
+                              element == EL_SP_ELECTRON ||
+                              element == EL_MOLE))
          DrawLevelField(x, y);
       }
     }
@@ -2424,15 +2791,19 @@ void StartMoving(int x, int y)
       {
        int phase = MovDelay[x][y] % 8;
 
-       if (phase>3)
-         phase = 7-phase;
+       if (phase > 3)
+         phase = 7 - phase;
 
        if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
-         DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element)+phase);
+         DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element) + phase);
 
-       if ((element == EL_MAMPFER || element == EL_MAMPFER2)
-           && MovDelay[x][y]%4 == 3)
-         PlaySoundLevel(x, y, SND_NJAM);
+       if (MovDelay[x][y] % 4 == 3)
+       {
+         if (element == EL_MAMPFER)
+           PlaySoundLevel(x, y, SND_YAMYAM_WAITING);
+         else if (element == EL_MAMPFER2)
+           PlaySoundLevel(x, y, SND_DARK_YAMYAM_WAITING);
+       }
       }
       else if (element == EL_SP_ELECTRON)
        DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
@@ -2483,43 +2854,74 @@ void StartMoving(int x, int y)
        return;
     }
 
-    if (element == EL_KAEFER || element == EL_BUTTERFLY)
-    {
-      PlaySoundLevel(x, y, SND_KLAPPER);
-    }
-    else if (element == EL_FLIEGER || element == EL_FIREFLY)
-    {
-      PlaySoundLevel(x, y, SND_ROEHR);
-    }
+    if (element == EL_KAEFER)
+      PlaySoundLevel(x, y, SND_BUG_MOVING);
+    else if (element == EL_FLIEGER)
+      PlaySoundLevel(x, y, SND_SPACESHIP_MOVING);
+    else if (element == EL_BUTTERFLY)
+      PlaySoundLevel(x, y, SND_BD_BUTTERFLY_MOVING);
+    else if (element == EL_FIREFLY)
+      PlaySoundLevel(x, y, SND_BD_FIREFLY_MOVING);
+    else if (element == EL_SP_SNIKSNAK)
+      PlaySoundLevel(x, y, SND_SP_SNIKSNAK_MOVING);
+    else if (element == EL_SP_ELECTRON)
+      PlaySoundLevel(x, y, SND_SP_ELECTRON_MOVING);
+    else if (element == EL_MAMPFER)
+      PlaySoundLevel(x, y, SND_YAMYAM_MOVING);
+    else if (element == EL_MAMPFER2)
+      PlaySoundLevel(x, y, SND_DARK_YAMYAM_MOVING);
+    else if (element == EL_BALLOON)
+      PlaySoundLevel(x, y, SND_BALLOON_MOVING);
+    else if (element == EL_SPRING_MOVING)
+      PlaySoundLevel(x, y, SND_SPRING_MOVING);
+    else if (element == EL_MOLE)
+      PlaySoundLevel(x, y, SND_MOLE_MOVING);
+    else if (element == EL_SONDE)
+      PlaySoundLevel(x, y, SND_SATELLITE_MOVING);
+    else if (element == EL_PACMAN)
+      PlaySoundLevel(x, y, SND_PACMAN_MOVING);
+    else if (element == EL_PINGUIN)
+      PlaySoundLevel(x, y, SND_PENGUIN_MOVING);
+    else if (element == EL_SCHWEIN)
+      PlaySoundLevel(x, y, SND_PIG_MOVING);
+    else if (element == EL_DRACHE)
+      PlaySoundLevel(x, y, SND_DRAGON_MOVING);
 
     /* now make next step */
 
     Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
 
-    if (IS_ENEMY(element) && IS_PLAYER(newx, newy))
+    if (IS_ENEMY(element) && IS_PLAYER(newx, newy) &&
+       !PLAYER_PROTECTED(newx, newy))
     {
+
+#if 1
+      TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+      return;
+#else
       /* enemy got the player */
       MovDir[x][y] = 0;
       KillHero(PLAYERINFO(newx, newy));
       return;
+#endif
+
     }
-    else if ((element == EL_MAULWURF || element == EL_PINGUIN ||
-             element == EL_ROBOT || element == EL_SONDE) &&
+    else if ((element == EL_PINGUIN || element == EL_ROBOT ||
+             element == EL_SONDE || element == EL_BALLOON) &&
             IN_LEV_FIELD(newx, newy) &&
             MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_SALZSAEURE)
     {
       Blurb(x, y);
       Store[x][y] = EL_SALZSAEURE;
     }
-    else if ((element == EL_MAULWURF || element == EL_PINGUIN) &&
-            IN_LEV_FIELD(newx, newy))
+    else if (element == EL_PINGUIN && IN_LEV_FIELD(newx, newy))
     {
       if (Feld[newx][newy] == EL_AUSGANG_AUF)
       {
        Feld[x][y] = EL_LEERRAUM;
        DrawLevelField(x, y);
 
-       PlaySoundLevel(newx, newy, SND_BUING);
+       PlaySoundLevel(newx, newy, SND_PENGUIN_ENTERING_EXIT);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
          DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2gfx(element));
 
@@ -2557,6 +2959,8 @@ void StartMoving(int x, int y)
          Feld[newx][newy] = EL_LEERRAUM;
          DrawLevelField(newx, newy);
        }
+
+       PlaySoundLevel(x, y, SND_PIG_EATING_GEM);
       }
       else if (!IS_FREE(newx, newy))
       {
@@ -2597,6 +3001,8 @@ void StartMoving(int x, int y)
          else
            DrawLevelField(x, y);
 
+         PlaySoundLevel(x, y, SND_DRAGON_BREATHING_FIRE);
+
          MovDelay[x][y] = 50;
          Feld[newx][newy] = EL_BURNING;
          if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_LEERRAUM)
@@ -2617,6 +3023,8 @@ void StartMoving(int x, int y)
        Feld[newx][newy] = EL_LEERRAUM;
        DrawLevelField(newx, newy);
       }
+
+      PlaySoundLevel(x, y, SND_YAMYAM_EATING_DIAMOND);
     }
     else if (element == EL_MAMPFER2 && IN_LEV_FIELD(newx, newy) &&
             IS_MAMPF2(Feld[newx][newy]))
@@ -2636,9 +3044,11 @@ void StartMoving(int x, int y)
        Feld[newx][newy] = EL_LEERRAUM;
        DrawLevelField(newx, newy);
       }
+
+      PlaySoundLevel(x, y, SND_DARK_YAMYAM_EATING_ANY);
     }
-    else if (element == EL_PACMAN && IN_LEV_FIELD(newx, newy) &&
-            IS_AMOEBOID(Feld[newx][newy]))
+    else if ((element == EL_PACMAN || element == EL_MOLE)
+            && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
     {
       if (AmoebaNr[newx][newy])
       {
@@ -2648,8 +3058,26 @@ void StartMoving(int x, int y)
          AmoebaCnt[AmoebaNr[newx][newy]]--;
       }
 
-      Feld[newx][newy] = EL_LEERRAUM;
-      DrawLevelField(newx, newy);
+      if (element == EL_MOLE)
+      {
+       Feld[newx][newy] = EL_DEAMOEBING;
+       PlaySoundLevel(x, y, SND_MOLE_EATING_AMOEBA);
+       MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
+       return;                         /* wait for shrinking amoeba */
+      }
+      else     /* element == EL_PACMAN */
+      {
+       Feld[newx][newy] = EL_LEERRAUM;
+       DrawLevelField(newx, newy);
+       PlaySoundLevel(x, y, SND_PACMAN_EATING_AMOEBA);
+      }
+    }
+    else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
+            (Feld[newx][newy] == EL_DEAMOEBING ||
+             (Feld[newx][newy] == EL_LEERRAUM && Stop[newx][newy])))
+    {
+      /* wait for shrinking amoeba to completely disappear */
+      return;
     }
     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
     {
@@ -2658,7 +3086,7 @@ void StartMoving(int x, int y)
       TurnRound(x, y);
 
       if (element == EL_KAEFER || element == EL_FLIEGER ||
-         element == EL_SP_SNIKSNAK)
+         element == EL_SP_SNIKSNAK || element == EL_MOLE)
        DrawLevelField(x, y);
       else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
        DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL);
@@ -2667,11 +3095,14 @@ void StartMoving(int x, int y)
       else if (element == EL_SP_ELECTRON)
        DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
 
+      if (DONT_TOUCH(element))
+       TestIfBadThingTouchesHero(x, y);
+
       return;
     }
 
     if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
-      PlaySoundLevel(x, y, SND_SCHLURF);
+      PlaySoundLevel(x, y, SND_ROBOT_MOVING);
 
     InitMovingField(x, y, MovDir[x][y]);
   }
@@ -2690,13 +3121,21 @@ void ContinueMoving(int x, int y)
   int newx = x + dx, newy = y + dy;
   int step = (horiz_move ? dx : dy) * TILEX / 8;
 
-  if (element == EL_TROPFEN)
+  if (element == EL_TROPFEN || element == EL_AMOEBA_DRIPPING)
     step /= 2;
-  else if (Store[x][y] == EL_MORAST_VOLL || Store[x][y] == EL_MORAST_LEER)
+  else if (element == EL_QUICKSAND_FILLING ||
+          element == EL_QUICKSAND_EMPTYING)
     step /= 4;
+  else if (element == EL_MAGIC_WALL_FILLING ||
+          element == EL_MAGIC_WALL_BD_FILLING ||
+          element == EL_MAGIC_WALL_EMPTYING ||
+          element == EL_MAGIC_WALL_BD_EMPTYING)
+    step /= 2;
   else if (CAN_FALL(element) && horiz_move &&
           y < lev_fieldy-1 && IS_BELT(Feld[x][y+1]))
     step /= 2;
+  else if (element == EL_SPRING_MOVING)
+    step*=2;
 
 #if OLD_GAME_BEHAVIOUR
   else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
@@ -2710,51 +3149,81 @@ void ContinueMoving(int x, int y)
     Feld[x][y] = EL_LEERRAUM;
     Feld[newx][newy] = element;
 
-    if (Store[x][y] == EL_MORAST_VOLL)
+    if (element == EL_MOLE)
     {
-      Store[x][y] = 0;
-      Feld[newx][newy] = EL_MORAST_VOLL;
-      element = EL_MORAST_VOLL;
+      int i;
+      static int xy[4][2] =
+      {
+       { 0, -1 },
+       { -1, 0 },
+       { +1, 0 },
+       { 0, +1 }
+      };
+
+      Feld[x][y] = EL_ERDREICH;
+      DrawLevelField(x, y);
+
+      for(i=0; i<4; i++)
+      {
+       int xx, yy;
+
+       xx = x + xy[i][0];
+       yy = y + xy[i][1];
+
+       if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_ERDREICH)
+         DrawLevelField(xx, yy);       /* for "ErdreichAnbroeckeln()" */
+      }
     }
-    else if (Store[x][y] == EL_MORAST_LEER)
+
+    if (element == EL_QUICKSAND_FILLING)
     {
-      Store[x][y] = 0;
-      Feld[x][y] = EL_MORAST_LEER;
+      element = Feld[newx][newy] = get_next_element(element);
+      Store[newx][newy] = Store[x][y];
     }
-    else if (Store[x][y] == EL_SIEB_VOLL)
+    else if (element == EL_QUICKSAND_EMPTYING)
     {
-      Store[x][y] = 0;
-      element = Feld[newx][newy] =
-       (game.magic_wall_active ? EL_SIEB_VOLL : EL_SIEB_TOT);
+      Feld[x][y] = get_next_element(element);
+      element = Feld[newx][newy] = Store[x][y];
     }
-    else if (Store[x][y] == EL_SIEB_LEER)
+    else if (element == EL_MAGIC_WALL_FILLING)
     {
-      Store[x][y] = Store2[x][y] = 0;
-      Feld[x][y] = (game.magic_wall_active ? EL_SIEB_LEER : EL_SIEB_TOT);
+      element = Feld[newx][newy] = get_next_element(element);
+      if (!game.magic_wall_active)
+       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
+      Store[newx][newy] = Store[x][y];
     }
-    else if (Store[x][y] == EL_SIEB2_VOLL)
+    else if (element == EL_MAGIC_WALL_EMPTYING)
     {
-      Store[x][y] = 0;
-      element = Feld[newx][newy] =
-       (game.magic_wall_active ? EL_SIEB2_VOLL : EL_SIEB2_TOT);
+      Feld[x][y] = get_next_element(element);
+      if (!game.magic_wall_active)
+       Feld[x][y] = EL_MAGIC_WALL_DEAD;
+      element = Feld[newx][newy] = Store[x][y];
     }
-    else if (Store[x][y] == EL_SIEB2_LEER)
+    else if (element == EL_MAGIC_WALL_BD_FILLING)
     {
-      Store[x][y] = Store2[x][y] = 0;
-      Feld[x][y] = (game.magic_wall_active ? EL_SIEB2_LEER : EL_SIEB2_TOT);
+      element = Feld[newx][newy] = get_next_element(element);
+      if (!game.magic_wall_active)
+       element = Feld[newx][newy] = EL_MAGIC_WALL_BD_DEAD;
+      Store[newx][newy] = Store[x][y];
     }
-    else if (Store[x][y] == EL_SALZSAEURE)
+    else if (element == EL_MAGIC_WALL_BD_EMPTYING)
     {
-      Store[x][y] = 0;
-      Feld[newx][newy] = EL_SALZSAEURE;
-      element = EL_SALZSAEURE;
+      Feld[x][y] = get_next_element(element);
+      if (!game.magic_wall_active)
+       Feld[x][y] = EL_MAGIC_WALL_BD_DEAD;
+      element = Feld[newx][newy] = Store[x][y];
     }
-    else if (Store[x][y] == EL_AMOEBE_NASS)
+    else if (element == EL_AMOEBA_DRIPPING)
     {
-      Store[x][y] = 0;
-      Feld[x][y] = EL_AMOEBE_NASS;
+      Feld[x][y] = get_next_element(element);
+      element = Feld[newx][newy] = Store[x][y];
+    }
+    else if (Store[x][y] == EL_SALZSAEURE)
+    {
+      element = Feld[newx][newy] = EL_SALZSAEURE;
     }
 
+    Store[x][y] = 0;
     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
     MovDelay[newx][newy] = 0;
 
@@ -2769,12 +3238,12 @@ void ContinueMoving(int x, int y)
 
     if (DONT_TOUCH(element))   /* object may be nasty to player or others */
     {
-      TestIfBadThingHitsHero(newx, newy);
-      TestIfBadThingHitsFriend(newx, newy);
-      TestIfBadThingHitsOtherBadThing(newx, newy);
+      TestIfBadThingTouchesHero(newx, newy);
+      TestIfBadThingTouchesFriend(newx, newy);
+      TestIfBadThingTouchesOtherBadThing(newx, newy);
     }
     else if (element == EL_PINGUIN)
-      TestIfFriendHitsBadThing(newx, newy);
+      TestIfFriendTouchesBadThing(newx, newy);
 
     if (CAN_SMASH(element) && direction == MV_DOWN &&
        (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
@@ -2890,6 +3359,9 @@ void AmoebeUmwandeln(int ax, int ay)
        }
       }
     }
+    PlaySoundLevel(ax, ay, (IS_GEM(level.amoeba_content) ?
+                           SND_AMOEBA_TURNING_TO_GEM :
+                           SND_AMOEBA_TURNING_TO_ROCK));
     Bang(ax, ay);
   }
   else
@@ -2911,7 +3383,12 @@ void AmoebeUmwandeln(int ax, int ay)
        continue;
 
       if (Feld[x][y] == EL_AMOEBA2DIAM)
+      {
+       PlaySoundLevel(x, y, (IS_GEM(level.amoeba_content) ?
+                             SND_AMOEBA_TURNING_TO_GEM :
+                             SND_AMOEBA_TURNING_TO_ROCK));
        Bang(x, y);
+      }
     }
   }
 }
@@ -2950,8 +3427,9 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
   }
 
   if (done)
-    PlaySoundLevel(ax, ay,
-                  (new_element == EL_FELSBROCKEN ? SND_KLOPF : SND_PLING));
+    PlaySoundLevel(ax, ay, (new_element == EL_BD_ROCK ?
+                           SND_BD_AMOEBA_TURNING_TO_ROCK :
+                           SND_BD_AMOEBA_TURNING_TO_GEM));
 }
 
 void AmoebeWaechst(int x, int y)
@@ -2965,7 +3443,10 @@ void AmoebeWaechst(int x, int y)
 
     if (DelayReached(&sound_delay, sound_delay_value))
     {
-      PlaySoundLevel(x, y, SND_AMOEBE);
+      if (Store[x][y] == EL_AMOEBE_BD)
+       PlaySoundLevel(x, y, SND_BD_AMOEBA_GROWING);
+      else
+       PlaySoundLevel(x, y, SND_AMOEBA_GROWING);
       sound_delay_value = 30;
     }
   }
@@ -2985,6 +3466,37 @@ void AmoebeWaechst(int x, int y)
   }
 }
 
+void AmoebaDisappearing(int x, int y)
+{
+  static unsigned long sound_delay = 0;
+  static unsigned long sound_delay_value = 0;
+
+  if (!MovDelay[x][y])         /* start new shrinking cycle */
+  {
+    MovDelay[x][y] = 7;
+
+    if (DelayReached(&sound_delay, sound_delay_value))
+      sound_delay_value = 30;
+  }
+
+  if (MovDelay[x][y])          /* wait some time before shrinking */
+  {
+    MovDelay[x][y]--;
+    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + MovDelay[x][y]/2);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_LEERRAUM;
+      DrawLevelField(x, y);
+
+      /* don't let mole enter this field in this cycle;
+        (give priority to objects falling to this field from above) */
+      Stop[x][y] = TRUE;
+    }
+  }
+}
+
 void AmoebeAbleger(int ax, int ay)
 {
   int i;
@@ -3101,7 +3613,7 @@ void AmoebeAbleger(int ax, int ay)
 
       if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200)
       {
-       AmoebeUmwandelnBD(newax, neway, EL_FELSBROCKEN);
+       AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
        return;
       }
     }
@@ -3110,16 +3622,19 @@ void AmoebeAbleger(int ax, int ay)
   if (element != EL_AMOEBE_NASS || neway < ay || !IS_FREE(newax, neway) ||
       (neway == lev_fieldy - 1 && newax != ax))
   {
-    Feld[newax][neway] = EL_AMOEBING;
+    Feld[newax][neway] = EL_AMOEBING;  /* simple growth of new amoeba tile */
     Store[newax][neway] = element;
   }
   else if (neway == ay)
-    Feld[newax][neway] = EL_TROPFEN;
+  {
+    Feld[newax][neway] = EL_TROPFEN;   /* drop left or right from amoeba */
+    PlaySoundLevel(newax, neway, SND_AMOEBA_DROPPING);
+  }
   else
   {
-    InitMovingField(ax, ay, MV_DOWN);
-    Feld[ax][ay] = EL_TROPFEN;
-    Store[ax][ay] = EL_AMOEBE_NASS;
+    InitMovingField(ax, ay, MV_DOWN);  /* drop dripping out of amoeba */
+    Feld[ax][ay] = EL_AMOEBA_DRIPPING;
+    Store[ax][ay] = EL_TROPFEN;
     ContinueMoving(ax, ay);
     return;
   }
@@ -3133,6 +3648,7 @@ void Life(int ax, int ay)
   static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
   int life_time = 40;
   int element = Feld[ax][ay];
+  boolean changed = FALSE;
 
   if (Stop[ax][ay])
     return;
@@ -3171,29 +3687,35 @@ void Life(int ax, int ay)
 
     if (xx == ax && yy == ay)          /* field in the middle */
     {
-      if (nachbarn<life[0] || nachbarn>life[1])
+      if (nachbarn < life[0] || nachbarn > life[1])
       {
        Feld[xx][yy] = EL_LEERRAUM;
        if (!Stop[xx][yy])
          DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
+       changed = TRUE;
       }
     }
     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH)
     {                                  /* free border field */
-      if (nachbarn>=life[2] && nachbarn<=life[3])
+      if (nachbarn >= life[2] && nachbarn <= life[3])
       {
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
          DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
+       changed = TRUE;
       }
     }
   }
+
+  if (changed)
+    PlaySoundLevel(ax, ay, element == EL_LIFE ? SND_GAMEOFLIFE_GROWING :
+                  SND_BIOMAZE_GROWING);
 }
 
-void Ablenk(int x, int y)
+void RobotWheel(int x, int y)
 {
   if (!MovDelay[x][y])         /* next animation frame */
     MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
@@ -3206,7 +3728,7 @@ void Ablenk(int x, int y)
       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
        DrawGraphic(SCREENX(x), SCREENY(y), GFX_ABLENK+MovDelay[x][y]%4);
       if (!(MovDelay[x][y]%4))
-       PlaySoundLevel(x, y, SND_MIEP);
+       PlaySoundLevel(x, y, SND_ROBOT_WHEEL_RUNNING);
       return;
     }
   }
@@ -3217,6 +3739,31 @@ void Ablenk(int x, int y)
     ZX = ZY = -1;
 }
 
+void TimegateWheel(int x, int y)
+{
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    MovDelay[x][y]--;
+    if (MovDelay[x][y])
+    {
+      if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+       DrawGraphic(SCREENX(x), SCREENY(y),
+                   GFX_TIMEGATE_SWITCH + MovDelay[x][y]%4);
+      if (!(MovDelay[x][y]%4))
+       PlaySoundLevel(x, y, SND_TIMEGATE_WHEEL_RUNNING);
+      return;
+    }
+  }
+
+  Feld[x][y] = EL_TIMEGATE_SWITCH_OFF;
+  DrawLevelField(x, y);
+  if (ZX == x && ZY == y)
+    ZX = ZY = -1;
+}
+
 void Birne(int x, int y)
 {
   if (!MovDelay[x][y])         /* next animation frame */
@@ -3263,7 +3810,8 @@ void NussKnacken(int x, int y)
   {
     MovDelay[x][y]--;
     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
-      DrawGraphic(SCREENX(x), SCREENY(y), GFX_CRACKINGNUT+3-MovDelay[x][y]/2);
+      DrawGraphic(SCREENX(x), SCREENY(y),
+                 GFX_CRACKINGNUT + 3 - MovDelay[x][y]/2);
 
     if (!MovDelay[x][y])
     {
@@ -3273,9 +3821,29 @@ void NussKnacken(int x, int y)
   }
 }
 
+void BreakingPearl(int x, int y)
+{
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 9;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    MovDelay[x][y]--;
+    if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y),
+                 GFX_PEARL_BREAKING + 4 - MovDelay[x][y]/2);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_LEERRAUM;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
 void SiebAktivieren(int x, int y, int typ)
 {
-  int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3;
+  int graphic = (typ == 1 ? GFX_MAGIC_WALL_FULL : GFX_MAGIC_WALL_BD_FULL) + 3;
 
   DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE);
 }
@@ -3292,7 +3860,7 @@ void AusgangstuerPruefen(int x, int y)
                   (x > LEVELX(BX2) ? LEVELX(BX2) : x),
                   y < LEVELY(BY1) ? LEVELY(BY1) :
                   (y > LEVELY(BY2) ? LEVELY(BY2) : y),
-                  SND_OEFFNEN);
+                  SND_EXIT_OPENING);
   }
 }
 
@@ -3373,6 +3941,73 @@ void CloseSwitchgate(int x, int y)
   }
 }
 
+void OpenTimegate(int x, int y)
+{
+  int delay = 6;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 5 * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+
+    MovDelay[x][y]--;
+    phase = MovDelay[x][y] / delay;
+    if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_TIMEGATE_OPEN - phase);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_TIMEGATE_OPEN;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
+void CloseTimegate(int x, int y)
+{
+  int delay = 6;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 5 * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+
+    MovDelay[x][y]--;
+    phase = MovDelay[x][y] / delay;
+    if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_TIMEGATE_CLOSED + phase);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_TIMEGATE_CLOSED;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
+static void CloseAllOpenTimegates()
+{
+  int x, y;
+
+  for (y=0; y<lev_fieldy; y++)
+  {
+    for (x=0; x<lev_fieldx; x++)
+    {
+      int element = Feld[x][y];
+
+      if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
+      {
+       Feld[x][y] = EL_TIMEGATE_CLOSING;
+       PlaySoundLevel(x, y, SND_TIMEGATE_CLOSING);
+      }
+    }
+  }
+}
+
 void EdelsteinFunkeln(int x, int y)
 {
   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
@@ -3410,8 +4045,8 @@ void EdelsteinFunkeln(int x, int y)
          dest_x = FX + SCREENX(x)*TILEX;
          dest_y = FY + SCREENY(y)*TILEY;
 
-         XCopyArea(display, drawto_field, window, gc,
-                   dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
+         BlitBitmap(drawto_field, window,
+                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
          SetDrawtoField(DRAW_DIRECT);
        }
       }
@@ -3477,6 +4112,7 @@ void MauerAbleger(int ax, int ay)
   boolean links_frei = FALSE, rechts_frei = FALSE;
   boolean oben_massiv = FALSE, unten_massiv = FALSE;
   boolean links_massiv = FALSE, rechts_massiv = FALSE;
+  boolean new_wall = FALSE;
 
   if (!MovDelay[ax][ay])       /* start building new wall */
     MovDelay[ax][ay] = 6;
@@ -3506,6 +4142,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax][ay-1] = MV_UP;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
        DrawGraphic(SCREENX(ax), SCREENY(ay-1), GFX_MAUER_UP);
+      new_wall = TRUE;
     }
     if (unten_frei)
     {
@@ -3514,6 +4151,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax][ay+1] = MV_DOWN;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
        DrawGraphic(SCREENX(ax), SCREENY(ay+1), GFX_MAUER_DOWN);
+      new_wall = TRUE;
     }
   }
 
@@ -3527,6 +4165,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax-1][ay] = MV_LEFT;
       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
        DrawGraphic(SCREENX(ax-1), SCREENY(ay), GFX_MAUER_LEFT);
+      new_wall = TRUE;
     }
     if (rechts_frei)
     {
@@ -3535,6 +4174,7 @@ void MauerAbleger(int ax, int ay)
       MovDir[ax+1][ay] = MV_RIGHT;
       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
        DrawGraphic(SCREENX(ax+1), SCREENY(ay), GFX_MAUER_RIGHT);
+      new_wall = TRUE;
     }
   }
 
@@ -3555,6 +4195,9 @@ void MauerAbleger(int ax, int ay)
       ((links_massiv && rechts_massiv) ||
        element == EL_MAUER_Y))
     Feld[ax][ay] = EL_MAUERWERK;
+
+  if (new_wall)
+    PlaySoundLevel(ax, ay, SND_WALL_GROWING);
 }
 
 void CheckForDragon(int x, int y)
@@ -3612,10 +4255,10 @@ static void CheckBuggyBase(int x, int y)
 
   if (element == EL_SP_BUG)
   {
-    if (!MovDelay[x][y])       /* start activating buggy base */
+    if (!MovDelay[x][y])       /* wait some time before activating base */
       MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
 
-    if (MovDelay[x][y])                /* wait some time before activating base */
+    if (MovDelay[x][y])
     {
       MovDelay[x][y]--;
       if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
@@ -3631,7 +4274,7 @@ static void CheckBuggyBase(int x, int y)
     if (!MovDelay[x][y])       /* start activating buggy base */
       MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
 
-    if (MovDelay[x][y])                /* wait some time before activating base */
+    if (MovDelay[x][y])
     {
       MovDelay[x][y]--;
       if (MovDelay[x][y])
@@ -3654,7 +4297,7 @@ static void CheckBuggyBase(int x, int y)
 
          if (IS_PLAYER(xx, yy))
          {
-           PlaySoundLevel(x, y, SND_SP_BUG);
+           PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_ACTIVATING);
            break;
          }
        }
@@ -3668,6 +4311,62 @@ static void CheckBuggyBase(int x, int y)
   }
 }
 
+static void CheckTrap(int x, int y)
+{
+  int element = Feld[x][y];
+
+  if (element == EL_TRAP_INACTIVE)
+  {
+    if (!MovDelay[x][y])       /* wait some time before activating trap */
+      MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
+
+    if (MovDelay[x][y])
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y])
+       return;
+
+      Feld[x][y] = EL_TRAP_ACTIVE;
+      PlaySoundLevel(x, y, SND_TRAP_ACTIVATING);
+    }
+  }
+  else if (element == EL_TRAP_ACTIVE)
+  {
+    int delay = 4;
+    int num_frames = 8;
+
+    if (!MovDelay[x][y])       /* start activating trap */
+      MovDelay[x][y] = num_frames * delay;
+
+    if (MovDelay[x][y])
+    {
+      MovDelay[x][y]--;
+
+      if (MovDelay[x][y])
+      {
+       if (!(MovDelay[x][y] % delay))
+       {
+         int phase = MovDelay[x][y]/delay;
+
+         if (phase >= num_frames/2)
+           phase = num_frames - phase;
+
+         if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+         {
+           DrawGraphic(SCREENX(x),SCREENY(y), GFX_TRAP_INACTIVE + phase - 1);
+           ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
+         }
+       }
+
+       return;
+      }
+
+      Feld[x][y] = EL_TRAP_INACTIVE;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
 static void DrawBeltAnimation(int x, int y, int element)
 {
   int belt_nr = getBeltNrFromElement(element);
@@ -3680,6 +4379,9 @@ static void DrawBeltAnimation(int x, int y, int element)
     int graphic = el2gfx(element) + (belt_dir == MV_LEFT ? 0 : 7);
 
     DrawGraphicAnimation(x, y, graphic, 8, delay, mode);
+
+    if (!(FrameCounter % 2))
+      PlaySoundLevel(x, y, SND_CONVEYOR_BELT_RUNNING);
   }
 }
 
@@ -3687,9 +4389,10 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
   static int num_stored_actions = 0;
+#if 0
   static boolean save_tape_entry = FALSE;
+#endif
   boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
-  int jx = player->jx, jy = player->jy;
   int left     = player_action & JOY_LEFT;
   int right    = player_action & JOY_RIGHT;
   int up       = player_action & JOY_UP;
@@ -3707,7 +4410,9 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 
   if (player_action)
   {
+#if 0
     save_tape_entry = TRUE;
+#endif
     player->frame_reset_delay = 0;
 
     if (button1)
@@ -3719,15 +4424,29 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
       moved = MoveFigure(player, dx, dy);
     }
 
+    if (tape.single_step && tape.recording && !tape.pausing)
+    {
+      if (button1 || (bombed && !moved))
+      {
+       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+       SnapField(player, 0, 0);                /* stop snapping */
+      }
+    }
+
+#if 0
     if (tape.recording && (moved || snapped || bombed))
     {
       if (bombed && !moved)
        player_action &= JOY_BUTTON;
 
       stored_player_action[player->index_nr] = player_action;
+      save_tape_entry = TRUE;
     }
     else if (tape.playing && snapped)
       SnapField(player, 0, 0);                 /* stop snapping */
+#else
+    stored_player_action[player->index_nr] = player_action;
+#endif
   }
   else
   {
@@ -3737,20 +4456,49 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
     SnapField(player, 0, 0);
     CheckGravityMovement(player);
 
+#if 1
+    if (player->MovPos == 0)   /* needed for tape.playing */
+      player->is_moving = FALSE;
+#endif
+#if 0
+    if (player->MovPos == 0)   /* needed for tape.playing */
+      player->last_move_dir = MV_NO_MOVING;
+
+    /* !!! CHECK THIS AGAIN !!!
+       (Seems to be needed for some EL_ROBOT stuff, but breaks
+       tapes when walking through pipes!)
+    */
+
+    /* it seems that "player->last_move_dir" is misused as some sort of
+       "player->is_just_moving_in_this_moment", which is needed for the
+       robot stuff (robots don't kill players when they are moving)
+    */
+#endif 
+
     if (++player->frame_reset_delay > player->move_delay_value)
       player->Frame = 0;
   }
 
+#if 0
   if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
   {
     TapeRecordAction(stored_player_action);
     num_stored_actions = 0;
     save_tape_entry = FALSE;
   }
+#else
+  if (tape.recording && num_stored_actions >= MAX_PLAYERS)
+  {
+    TapeRecordAction(stored_player_action);
+    num_stored_actions = 0;
+  }
+#endif
 
+#if 0
   if (tape.playing && !tape.pausing && !player_action &&
       tape.counter < tape.length)
   {
+    int jx = player->jx, jy = player->jy;
     int next_joy =
       tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
 
@@ -3762,7 +4510,8 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
       if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
       {
        int el = Feld[jx+dx][jy];
-       int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : 10);
+       int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 :
+                         (el == EL_BALLOON || el == EL_SPRING) ? 0 : 10);
 
        if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
        {
@@ -3773,6 +4522,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
       }
     }
   }
+#endif
 }
 
 void GameActions()
@@ -3790,6 +4540,9 @@ void GameActions()
   action_delay_value =
     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
 
+  if (tape.playing && tape.index_search && !tape.pausing)
+    action_delay_value = 0;
+
   /* ---------- main game synchronization point ---------- */
 
   WaitUntilDelayReached(&action_delay, action_delay_value);
@@ -3802,7 +4555,7 @@ void GameActions()
 #endif
     */
 
-#ifndef MSDOS
+#if defined(PLATFORM_UNIX)
     /* last chance to get network player actions without main loop delay */
     HandleNetworking();
 #endif
@@ -3824,11 +4577,6 @@ void GameActions()
   if (tape.pausing)
     return;
 
-  if (tape.playing)
-    TapePlayDelay();
-  else if (tape.recording)
-    TapeRecordDelay();
-
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
   for (i=0; i<MAX_PLAYERS; i++)
@@ -3839,7 +4587,7 @@ void GameActions()
       stored_player[i].effective_action = stored_player[i].action;
   }
 
-#ifndef MSDOS
+#if defined(PLATFORM_UNIX)
   if (network_playing)
     SendToServer_MovePlayer(summarized_player_action);
 #endif
@@ -3925,29 +4673,41 @@ void GameActions()
     {
       StartMoving(x, y);
 
-      if (IS_GEM(element))
+      if (IS_GEM(element) || element == EL_SP_INFOTRON)
        EdelsteinFunkeln(x, y);
     }
     else if (IS_MOVING(x, y))
       ContinueMoving(x, y);
     else if (IS_ACTIVE_BOMB(element))
       CheckDynamite(x, y);
-    else if (element == EL_EXPLODING)
+#if 0
+    else if (element == EL_EXPLODING && !game.explosions_delayed)
       Explode(x, y, Frame[x][y], EX_NORMAL);
+#endif
     else if (element == EL_AMOEBING)
       AmoebeWaechst(x, y);
+    else if (element == EL_DEAMOEBING)
+      AmoebaDisappearing(x, y);
+
+#if !USE_NEW_AMOEBA_CODE
     else if (IS_AMOEBALIVE(element))
       AmoebeAbleger(x, y);
+#endif
+
     else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
       Life(x, y);
     else if (element == EL_ABLENK_EIN)
-      Ablenk(x, y);
+      RobotWheel(x, y);
+    else if (element == EL_TIMEGATE_SWITCH_ON)
+      TimegateWheel(x, y);
     else if (element == EL_SALZSAEURE)
       Blubber(x, y);
     else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT)
       Blurb(x, y);
     else if (element == EL_CRACKINGNUT)
       NussKnacken(x, y);
+    else if (element == EL_PEARL_BREAKING)
+      BreakingPearl(x, y);
     else if (element == EL_AUSGANG_ZU)
       AusgangstuerPruefen(x, y);
     else if (element == EL_AUSGANG_ACT)
@@ -3965,6 +4725,8 @@ void GameActions()
       CheckForDragon(x, y);
     else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
       CheckBuggyBase(x, y);
+    else if (element == EL_TRAP_INACTIVE || element == EL_TRAP_ACTIVE)
+      CheckTrap(x, y);
     else if (element == EL_SP_TERMINAL)
       DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
     else if (element == EL_SP_TERMINAL_ACTIVE)
@@ -3975,24 +4737,40 @@ void GameActions()
       OpenSwitchgate(x, y);
     else if (element == EL_SWITCHGATE_CLOSING)
       CloseSwitchgate(x, y);
+    else if (element == EL_TIMEGATE_OPENING)
+      OpenTimegate(x, y);
+    else if (element == EL_TIMEGATE_CLOSING)
+      CloseTimegate(x, y);
     else if (element == EL_EXTRA_TIME)
       DrawGraphicAnimation(x, y, GFX_EXTRA_TIME, 6, 4, ANIM_NORMAL);
-    else if (element == EL_FORCE_FIELD)
-      DrawGraphicAnimation(x, y, GFX_FORCE_FIELD, 6, 4, ANIM_NORMAL);
+    else if (element == EL_SHIELD_PASSIVE)
+    {
+      DrawGraphicAnimation(x, y, GFX_SHIELD_PASSIVE, 6, 4, ANIM_NORMAL);
+      if (!(FrameCounter % 4))
+       PlaySoundLevel(x, y, SND_SHIELD_PASSIVE_ACTIVATED);
+    }
+    else if (element == EL_SHIELD_ACTIVE)
+    {
+      DrawGraphicAnimation(x, y, GFX_SHIELD_ACTIVE, 6, 4, ANIM_NORMAL);
+      if (!(FrameCounter % 4))
+       PlaySoundLevel(x, y, SND_SHIELD_ACTIVE_ACTIVATED);
+    }
 
     if (game.magic_wall_active)
     {
       boolean sieb = FALSE;
       int jx = local_player->jx, jy = local_player->jy;
 
-      if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL ||
-         Store[x][y] == EL_SIEB_LEER)
+      if (element == EL_MAGIC_WALL_FULL ||
+         element == EL_MAGIC_WALL_EMPTY ||
+         element == EL_MAGIC_WALL_EMPTYING)
       {
        SiebAktivieren(x, y, 1);
        sieb = TRUE;
       }
-      else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL ||
-              Store[x][y] == EL_SIEB2_LEER)
+      else if (element == EL_MAGIC_WALL_BD_FULL ||
+              element == EL_MAGIC_WALL_BD_EMPTY ||
+              element == EL_MAGIC_WALL_BD_EMPTYING)
       {
        SiebAktivieren(x, y, 2);
        sieb = TRUE;
@@ -4007,10 +4785,78 @@ void GameActions()
     }
   }
 
+#if USE_NEW_AMOEBA_CODE
+  /* new experimental amoeba growth stuff */
+#if 1
+  if (!(FrameCounter % 8))
+#endif
+  {
+    static unsigned long random = 1684108901;
+
+    for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
+    {
+#if 0
+      x = (random >> 10) % lev_fieldx;
+      y = (random >> 20) % lev_fieldy;
+#else
+      x = RND(lev_fieldx);
+      y = RND(lev_fieldy);
+#endif
+      element = Feld[x][y];
+
+      if (!IS_PLAYER(x,y) &&
+         (element == EL_LEERRAUM ||
+          element == EL_ERDREICH ||
+          element == EL_MORAST_LEER ||
+          element == EL_BLURB_LEFT ||
+          element == EL_BLURB_RIGHT))
+      {
+       if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBE_NASS))
+         Feld[x][y] = EL_TROPFEN;
+      }
+
+      random = random * 129 + 1;
+    }
+  }
+#endif
+
+#if 0
+  if (game.explosions_delayed)
+#endif
+  {
+    game.explosions_delayed = FALSE;
+
+    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    {
+      element = Feld[x][y];
+
+      if (ExplodeField[x][y])
+       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
+      else if (element == EL_EXPLODING)
+       Explode(x, y, Frame[x][y], EX_NORMAL);
+
+      ExplodeField[x][y] = EX_NO_EXPLOSION;
+    }
+
+    game.explosions_delayed = TRUE;
+  }
+
   if (game.magic_wall_active)
   {
     if (!(game.magic_wall_time_left % 4))
-      PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
+    {
+      int element = Feld[sieb_x][sieb_y];
+
+      if (element == EL_MAGIC_WALL_BD_FULL ||
+         element == EL_MAGIC_WALL_BD_EMPTY ||
+         element == EL_MAGIC_WALL_BD_EMPTYING)
+       PlaySoundLevel(sieb_x, sieb_y, SND_BD_MAGIC_WALL_RUNNING);
+      else
+       PlaySoundLevel(sieb_x, sieb_y, SND_MAGIC_WALL_RUNNING);
+    }
 
     if (game.magic_wall_time_left > 0)
     {
@@ -4021,14 +4867,16 @@ void GameActions()
        {
          element = Feld[x][y];
 
-         if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
+         if (element == EL_MAGIC_WALL_EMPTY ||
+             element == EL_MAGIC_WALL_FULL)
          {
-           Feld[x][y] = EL_SIEB_TOT;
+           Feld[x][y] = EL_MAGIC_WALL_DEAD;
            DrawLevelField(x, y);
          }
-         else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
+         else if (element == EL_MAGIC_WALL_BD_EMPTY ||
+                  element == EL_MAGIC_WALL_BD_FULL)
          {
-           Feld[x][y] = EL_SIEB2_TOT;
+           Feld[x][y] = EL_MAGIC_WALL_BD_DEAD;
            DrawLevelField(x, y);
          }
        }
@@ -4061,11 +4909,30 @@ void GameActions()
     }
   }
 
-  if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
+  if (game.timegate_time_left > 0)
+  {
+    game.timegate_time_left--;
+
+    if (game.timegate_time_left == 0)
+      CloseAllOpenTimegates();
+  }
+
+  if (TimeFrames >= (1000 / GameFrameDelay))
   {
     TimeFrames = 0;
     TimePlayed++;
 
+    for (i=0; i<MAX_PLAYERS; i++)
+    {
+      if (SHIELD_ON(&stored_player[i]))
+      {
+       stored_player[i].shield_passive_time_left--;
+
+       if (stored_player[i].shield_active_time_left > 0)
+         stored_player[i].shield_active_time_left--;
+      }
+    }
+
     if (tape.recording || tape.playing)
       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
 
@@ -4073,20 +4940,39 @@ void GameActions()
     {
       TimeLeft--;
 
-      if (TimeLeft <= 10)
-       PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      if (TimeLeft <= 10 && setup.time_limit)
+       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MAX_RIGHT);
 
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
-      if (!TimeLeft)
+      if (!TimeLeft && setup.time_limit)
        for (i=0; i<MAX_PLAYERS; i++)
          KillHero(&stored_player[i]);
     }
-    else if (level.time == 0)          /* level without time limit */
+    else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
   }
 
   DrawAllPlayers();
+
+  if (options.debug)                   /* calculate frames per second */
+  {
+    static unsigned long fps_counter = 0;
+    static int fps_frames = 0;
+    unsigned long fps_delay_ms = Counter() - fps_counter;
+
+    fps_frames++;
+
+    if (fps_delay_ms >= 500)   /* calculate fps every 0.5 seconds */
+    {
+      global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
+
+      fps_frames = 0;
+      fps_counter = Counter();
+    }
+
+    redraw_mask |= REDRAW_FPS;
+  }
 }
 
 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
@@ -4133,13 +5019,13 @@ void ScrollLevel(int dx, int dy)
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
 
-  XCopyArea(display, drawto_field, drawto_field, gc,
-           FX + TILEX*(dx == -1) - softscroll_offset,
-           FY + TILEY*(dy == -1) - softscroll_offset,
-           SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
-           SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
-           FX + TILEX*(dx == 1) - softscroll_offset,
-           FY + TILEY*(dy == 1) - softscroll_offset);
+  BlitBitmap(drawto_field, drawto_field,
+            FX + TILEX*(dx == -1) - softscroll_offset,
+            FY + TILEY*(dy == -1) - softscroll_offset,
+            SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
+            SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
+            FX + TILEX*(dx == 1) - softscroll_offset,
+            FY + TILEY*(dy == 1) - softscroll_offset);
 
   if (dx)
   {
@@ -4223,7 +5109,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
       BuryHero(player);
     }
     else
-      KillHero(player);
+      TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
 
     return MF_MOVING;
   }
@@ -4256,9 +5142,15 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
   if (!player->active || (!dx && !dy))
     return FALSE;
 
+#if 0
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
       !tape.playing)
     return FALSE;
+#else
+  if (!FrameReached(&player->move_delay, player->move_delay_value) &&
+      !(tape.playing && tape.game_version < GAME_VERSION_2_0))
+    return FALSE;
+#endif
 
   /* remove the last programmed player action */
   player->programmed_action = 0;
@@ -4387,15 +5279,19 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     DrawLevelField(jx, jy);    /* for "ErdreichAnbroeckeln()" */
 
     player->last_move_dir = player->MovDir;
+    player->is_moving = TRUE;
   }
   else
   {
     CheckGravityMovement(player);
 
+    /*
     player->last_move_dir = MV_NO_MOVING;
+    */
+    player->is_moving = FALSE;
   }
 
-  TestIfHeroHitsBadThing(jx, jy);
+  TestIfHeroTouchesBadThing(jx, jy);
 
   if (!player->active)
     RemoveHero(player);
@@ -4459,6 +5355,10 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
       if (!local_player->friends_still_needed)
        player->LevelSolved = player->GameOver = TRUE;
     }
+
+    if (tape.single_step && tape.recording && !tape.pausing &&
+       !player->programmed_action)
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
   }
 }
 
@@ -4490,17 +5390,17 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
     ScreenMovDir = MV_NO_MOVING;
 }
 
-void TestIfGoodThingHitsBadThing(int goodx, int goody)
+void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
-  int i, killx = goodx, killy = goody;
-  static int xy[4][2] =
+  int i, kill_x = -1, kill_y = -1;
+  static int test_xy[4][2] =
   {
     { 0, -1 },
     { -1, 0 },
     { +1, 0 },
     { 0, +1 }
   };
-  static int harmless[4] =
+  static int test_dir[4] =
   {
     MV_UP,
     MV_LEFT,
@@ -4510,50 +5410,62 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
 
   for (i=0; i<4; i++)
   {
-    int x, y, element;
+    int test_x, test_y, test_move_dir, test_element;
 
-    x = goodx + xy[i][0];
-    y = goody + xy[i][1];
-    if (!IN_LEV_FIELD(x, y))
+    test_x = good_x + test_xy[i][0];
+    test_y = good_y + test_xy[i][1];
+    if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
+    test_move_dir =
+      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+
 #if 0
-    element = Feld[x][y];
+    test_element = Feld[test_x][test_y];
 #else
-    element = MovingOrBlocked2ElementIfNotLeaving(x, y);
+    test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
 #endif
 
-    if (DONT_TOUCH(element))
+    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_GO_TO(test_element) && good_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
     {
-      if (MovDir[x][y] == harmless[i])
-       continue;
-
-      killx = x;
-      killy = y;
+      kill_x = test_x;
+      kill_y = test_y;
       break;
     }
   }
 
-  if (killx != goodx || killy != goody)
+  if (kill_x != -1 || kill_y != -1)
   {
-    if (IS_PLAYER(goodx, goody))
-      KillHero(PLAYERINFO(goodx, goody));
+    if (IS_PLAYER(good_x, good_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
+
+      if (player->shield_active_time_left > 0)
+       Bang(kill_x, kill_y);
+      else if (!PLAYER_PROTECTED(good_x, good_y))
+       KillHero(player);
+    }
     else
-      Bang(goodx, goody);
+      Bang(good_x, good_y);
   }
 }
 
-void TestIfBadThingHitsGoodThing(int badx, int bady)
+void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 {
-  int i, killx = badx, killy = bady;
-  static int xy[4][2] =
+  int i, kill_x = -1, kill_y = -1;
+  int bad_element = Feld[bad_x][bad_y];
+  static int test_xy[4][2] =
   {
     { 0, -1 },
     { -1, 0 },
     { +1, 0 },
     { 0, +1 }
   };
-  static int harmless[4] =
+  static int test_dir[4] =
   {
     MV_UP,
     MV_LEFT,
@@ -4561,66 +5473,117 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     MV_DOWN
   };
 
+  if (bad_element == EL_EXPLODING)     /* skip just exploding bad things */
+    return;
+
   for (i=0; i<4; i++)
   {
-    int x, y, element;
+    int test_x, test_y, test_move_dir, test_element;
 
-    x = badx + xy[i][0];
-    y = bady + xy[i][1];
-    if (!IN_LEV_FIELD(x, y))
+    test_x = bad_x + test_xy[i][0];
+    test_y = bad_y + test_xy[i][1];
+    if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
-    element = Feld[x][y];
+    test_move_dir =
+      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
 
-    if (IS_PLAYER(x, y))
-    {
-      killx = x;
-      killy = y;
-      break;
-    }
-    else if (element == EL_PINGUIN)
+    test_element = Feld[test_x][test_y];
+
+    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_GO_TO(bad_element) &&  bad_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
     {
-      if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
-       continue;
+      /* good thing is player or penguin that does not move away */
+      if (IS_PLAYER(test_x, test_y))
+      {
+       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
 
-      killx = x;
-      killy = y;
-      break;
+       if (bad_element == EL_ROBOT && player->is_moving)
+         continue;     /* robot does not kill player if he is moving */
+
+       kill_x = test_x;
+       kill_y = test_y;
+       break;
+      }
+      else if (test_element == EL_PINGUIN)
+      {
+       kill_x = test_x;
+       kill_y = test_y;
+       break;
+      }
     }
   }
 
-  if (killx != badx || killy != bady)
+  if (kill_x != -1 || kill_y != -1)
   {
-    if (IS_PLAYER(killx, killy))
-      KillHero(PLAYERINFO(killx, killy));
+    if (IS_PLAYER(kill_x, kill_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+#if 0
+      int dir = player->MovDir;
+      int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+      int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
+
+      if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
+         newx != bad_x && newy != bad_y)
+       ;       /* robot does not kill player if he is moving */
+      else
+       printf("-> %d\n", player->MovDir);
+
+      if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
+         newx != bad_x && newy != bad_y)
+       ;       /* robot does not kill player if he is moving */
+      else
+       ;
+#endif
+
+      if (player->shield_active_time_left > 0)
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_PROTECTED(kill_x, kill_y))
+       KillHero(player);
+    }
     else
-      Bang(killx, killy);
+      Bang(kill_x, kill_y);
   }
 }
 
-void TestIfHeroHitsBadThing(int x, int y)
+void TestIfHeroTouchesBadThing(int x, int y)
+{
+  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+}
+
+void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
+{
+  TestIfGoodThingHitsBadThing(x, y, move_dir);
+}
+
+void TestIfBadThingTouchesHero(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsHero(int x, int y)
+void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
 {
-  TestIfBadThingHitsGoodThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, move_dir);
 }
 
-void TestIfFriendHitsBadThing(int x, int y)
+void TestIfFriendTouchesBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y);
+  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsFriend(int x, int y)
+void TestIfBadThingTouchesFriend(int x, int y)
 {
-  TestIfBadThingHitsGoodThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsOtherBadThing(int badx, int bady)
+void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
 {
-  int i, killx = badx, killy = bady;
+  int i, kill_x = bad_x, kill_y = bad_y;
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -4633,8 +5596,8 @@ void TestIfBadThingHitsOtherBadThing(int badx, int bady)
   {
     int x, y, element;
 
-    x=badx + xy[i][0];
-    y=bady + xy[i][1];
+    x = bad_x + xy[i][0];
+    y = bad_y + xy[i][1];
     if (!IN_LEV_FIELD(x, y))
       continue;
 
@@ -4642,14 +5605,14 @@ void TestIfBadThingHitsOtherBadThing(int badx, int bady)
     if (IS_AMOEBOID(element) || element == EL_LIFE ||
        element == EL_AMOEBING || element == EL_TROPFEN)
     {
-      killx = x;
-      killy = y;
+      kill_x = x;
+      kill_y = y;
       break;
     }
   }
 
-  if (killx != badx || killy != bady)
-    Bang(badx, bady);
+  if (kill_x != bad_x || kill_y != bad_y)
+    Bang(bad_x, bad_y);
 }
 
 void KillHero(struct PlayerInfo *player)
@@ -4662,10 +5625,20 @@ void KillHero(struct PlayerInfo *player)
   if (IS_PFORTE(Feld[jx][jy]))
     Feld[jx][jy] = EL_LEERRAUM;
 
+  /* deactivate shield (else Bang()/Explode() would not work right) */
+  player->shield_passive_time_left = 0;
+  player->shield_active_time_left = 0;
+
   Bang(jx, jy);
   BuryHero(player);
 }
 
+static void KillHeroUnlessProtected(int x, int y)
+{
+  if (!PLAYER_PROTECTED(x, y))
+    KillHero(PLAYERINFO(x, y));
+}
+
 void BuryHero(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
@@ -4673,8 +5646,8 @@ void BuryHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
-  PlaySoundLevel(jx, jy, SND_AUTSCH);
-  PlaySoundLevel(jx, jy, SND_LACHEN);
+  PlaySoundLevel(jx, jy, SND_PLAYER_DYING);
+  PlaySoundLevel(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
   RemoveHero(player);
@@ -4688,7 +5661,8 @@ void RemoveHero(struct PlayerInfo *player)
   player->present = FALSE;
   player->active = FALSE;
 
-  StorePlayer[jx][jy] = 0;
+  if (!ExplodeField[jx][jy])
+    StorePlayer[jx][jy] = 0;
 
   for (i=0; i<MAX_PLAYERS; i++)
     if (stored_player[i].active)
@@ -4725,24 +5699,60 @@ int DigField(struct PlayerInfo *player,
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
     return MF_NO_ACTION;
 
+  if (IS_TUBE(Feld[jx][jy]))
+  {
+    int i = 0;
+    int tube_leave_directions[][2] =
+    {
+      { EL_TUBE_CROSS,         MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+      { EL_TUBE_VERTICAL,                           MV_UP | MV_DOWN },
+      { EL_TUBE_HORIZONTAL,    MV_LEFT | MV_RIGHT                   },
+      { EL_TUBE_VERT_LEFT,     MV_LEFT |            MV_UP | MV_DOWN },
+      { EL_TUBE_VERT_RIGHT,              MV_RIGHT | MV_UP | MV_DOWN },
+      { EL_TUBE_HORIZ_UP,      MV_LEFT | MV_RIGHT | MV_UP           },
+      { EL_TUBE_HORIZ_DOWN,    MV_LEFT | MV_RIGHT |         MV_DOWN },
+      { EL_TUBE_LEFT_UP,       MV_LEFT |            MV_UP           },
+      { EL_TUBE_LEFT_DOWN,     MV_LEFT |                    MV_DOWN },
+      { EL_TUBE_RIGHT_UP,                MV_RIGHT | MV_UP           },
+      { EL_TUBE_RIGHT_DOWN,              MV_RIGHT |         MV_DOWN },
+      { -1,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
+    };
+
+    while (tube_leave_directions[i][0] != Feld[jx][jy])
+    {
+      i++;
+      if (tube_leave_directions[i][0] == -1)   /* should not happen */
+       break;
+    }
+
+    if (!(tube_leave_directions[i][1] & move_direction))
+      return MF_NO_ACTION;     /* tube has no opening in this direction */
+  }
+
   element = Feld[x][y];
 
   switch (element)
   {
     case EL_LEERRAUM:
-      PlaySoundLevel(x, y, SND_EMPTY);
-      break;
-
     case EL_ERDREICH:
     case EL_SAND_INVISIBLE:
-      Feld[x][y] = EL_LEERRAUM;
-      PlaySoundLevel(x, y, SND_SCHLURF);
-      break;
-
+    case EL_TRAP_INACTIVE:
     case EL_SP_BASE:
     case EL_SP_BUG:
-      Feld[x][y] = EL_LEERRAUM;
-      PlaySoundLevel(x, y, SND_SP_BASE);
+      RemoveField(x, y);
+
+      if (element == EL_LEERRAUM)
+       PlaySoundLevel(x, y, SND_EMPTY_SPACE_DIGGING);
+      else if (element == EL_ERDREICH)
+       PlaySoundLevel(x, y, SND_SAND_DIGGING);
+      else if (element == EL_SAND_INVISIBLE)
+       PlaySoundLevel(x, y, SND_SAND_INVISIBLE_DIGGING);
+      else if (element == EL_TRAP_INACTIVE)
+       PlaySoundLevel(x, y, SND_TRAP_INACTIVE_DIGGING);
+      else if (element == EL_SP_BASE)
+       PlaySoundLevel(x, y, SND_SP_BASE_DIGGING);
+      else if (element == EL_SP_BUG)
+       PlaySoundLevel(x, y, SND_SP_BUGGY_BASE_DIGGING);
       break;
 
     case EL_EDELSTEIN:
@@ -4752,29 +5762,42 @@ int DigField(struct PlayerInfo *player,
     case EL_EDELSTEIN_LILA:
     case EL_DIAMANT:
     case EL_SP_INFOTRON:
+    case EL_PEARL:
+    case EL_CRYSTAL:
       RemoveField(x, y);
-      local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1);
+      local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 :
+                                         element == EL_PEARL ? 5 :
+                                         element == EL_CRYSTAL ? 8 : 1);
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
       RaiseScoreElement(element);
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3),
               FS_SMALL, FC_YELLOW);
-      if (element == EL_SP_INFOTRON)
-       PlaySoundLevel(x, y, SND_SP_INFOTRON);
-      else
-       PlaySoundLevel(x, y, SND_PONG);
+
+      if (element == EL_EDELSTEIN_BD)
+       PlaySoundLevel(x, y, SND_BD_DIAMOND_COLLECTING);
+      else if (element == EL_DIAMANT)
+       PlaySoundLevel(x, y, SND_DIAMOND_COLLECTING);
+      else if (element == EL_SP_INFOTRON)
+       PlaySoundLevel(x, y, SND_SP_INFOTRON_COLLECTING);
+      else if (element == EL_PEARL)
+       PlaySoundLevel(x, y, SND_PEARL_COLLECTING);
+      else if (element == EL_CRYSTAL)
+       PlaySoundLevel(x, y, SND_CRYSTAL_COLLECTING);
+      else     /* EL_EDELSTEIN style element */
+       PlaySoundLevel(x, y, SND_EMERALD_COLLECTING);
       break;
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
       break;
 
     case EL_ENVELOPE:
       Feld[x][y] = EL_LEERRAUM;
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
       break;
 
     case EL_EXTRA_TIME:
@@ -4784,12 +5807,20 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       }
-      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MAX_RIGHT);
       break;
 
-    case EL_FORCE_FIELD:
+    case EL_SHIELD_PASSIVE:
       RemoveField(x, y);
-      PlaySoundLevel(x, y, SND_PONG);
+      player->shield_passive_time_left += 10;
+      PlaySoundLevel(x, y, SND_SHIELD_PASSIVE_COLLECTING);
+      break;
+
+    case EL_SHIELD_ACTIVE:
+      RemoveField(x, y);
+      player->shield_passive_time_left += 10;
+      player->shield_active_time_left += 10;
+      PlaySoundLevel(x, y, SND_SHIELD_ACTIVE_COLLECTING);
       break;
 
     case EL_DYNAMITE_INACTIVE:
@@ -4801,9 +5832,9 @@ int DigField(struct PlayerInfo *player,
               int2str(local_player->dynamite, 3),
               FS_SMALL, FC_YELLOW);
       if (element == EL_SP_DISK_RED)
-       PlaySoundLevel(x, y, SND_SP_INFOTRON);
+       PlaySoundLevel(x, y, SND_SP_DISK_RED_COLLECTING);
       else
-       PlaySoundLevel(x, y, SND_PONG);
+       PlaySoundLevel(x, y, SND_DYNAMITE_COLLECTING);
       break;
 
     case EL_DYNABOMB_NR:
@@ -4811,21 +5842,21 @@ int DigField(struct PlayerInfo *player,
       player->dynabomb_count++;
       player->dynabombs_left++;
       RaiseScoreElement(EL_DYNAMITE_INACTIVE);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_DYNABOMB_NR_COLLECTING);
       break;
 
     case EL_DYNABOMB_SZ:
       RemoveField(x, y);
       player->dynabomb_size++;
       RaiseScoreElement(EL_DYNAMITE_INACTIVE);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_DYNABOMB_SZ_COLLECTING);
       break;
 
     case EL_DYNABOMB_XL:
       RemoveField(x, y);
       player->dynabomb_xl = TRUE;
       RaiseScoreElement(EL_DYNAMITE_INACTIVE);
-      PlaySoundLevel(x, y, SND_PONG);
+      PlaySoundLevel(x, y, SND_DYNABOMB_XL_COLLECTING);
       break;
 
     case EL_SCHLUESSEL1:
@@ -4838,13 +5869,11 @@ int DigField(struct PlayerInfo *player,
       RemoveField(x, y);
       player->key[key_nr] = TRUE;
       RaiseScoreElement(EL_SCHLUESSEL);
-      DrawMiniGraphicExt(drawto, gc,
-                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
-                        GFX_SCHLUESSEL1+key_nr);
-      DrawMiniGraphicExt(window, gc,
-                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
-                        GFX_SCHLUESSEL1+key_nr);
-      PlaySoundLevel(x, y, SND_PONG);
+      DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1 + key_nr);
+      DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1 + key_nr);
+      PlaySoundLevel(x, y, SND_KEY_COLLECTING);
       break;
     }
 
@@ -4858,13 +5887,11 @@ int DigField(struct PlayerInfo *player,
       RemoveField(x, y);
       player->key[key_nr] = TRUE;
       RaiseScoreElement(EL_SCHLUESSEL);
-      DrawMiniGraphicExt(drawto, gc,
-                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
-                        GFX_SCHLUESSEL1+key_nr);
-      DrawMiniGraphicExt(window, gc,
-                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
-                        GFX_SCHLUESSEL1+key_nr);
-      PlaySoundLevel(x, y, SND_PONG);
+      DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1 + key_nr);
+      DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1 + key_nr);
+      PlaySoundLevel(x, y, SND_KEY_COLLECTING);
       break;
     }
 
@@ -4873,6 +5900,7 @@ int DigField(struct PlayerInfo *player,
       ZX = x;
       ZY = y;
       DrawLevelField(x, y);
+      PlaySoundLevel(x, y, SND_ROBOT_WHEEL_ACTIVATING);
       return MF_ACTION;
       break;
 
@@ -4880,6 +5908,8 @@ int DigField(struct PlayerInfo *player,
       {
        int xx, yy;
 
+       PlaySoundLevel(x, y, SND_SP_TERMINAL_ACTIVATING);
+
        for (yy=0; yy<lev_fieldy; yy++)
        {
          for (xx=0; xx<lev_fieldx; xx++)
@@ -4895,22 +5925,23 @@ int DigField(struct PlayerInfo *player,
       }
       break;
 
-    case EL_BELT1_SWITCH_L:
-    case EL_BELT1_SWITCH_M:
-    case EL_BELT1_SWITCH_R:
-    case EL_BELT2_SWITCH_L:
-    case EL_BELT2_SWITCH_M:
-    case EL_BELT2_SWITCH_R:
-    case EL_BELT3_SWITCH_L:
-    case EL_BELT3_SWITCH_M:
-    case EL_BELT3_SWITCH_R:
-    case EL_BELT4_SWITCH_L:
-    case EL_BELT4_SWITCH_M:
-    case EL_BELT4_SWITCH_R:
+    case EL_BELT1_SWITCH_LEFT:
+    case EL_BELT1_SWITCH_MIDDLE:
+    case EL_BELT1_SWITCH_RIGHT:
+    case EL_BELT2_SWITCH_LEFT:
+    case EL_BELT2_SWITCH_MIDDLE:
+    case EL_BELT2_SWITCH_RIGHT:
+    case EL_BELT3_SWITCH_LEFT:
+    case EL_BELT3_SWITCH_MIDDLE:
+    case EL_BELT3_SWITCH_RIGHT:
+    case EL_BELT4_SWITCH_LEFT:
+    case EL_BELT4_SWITCH_MIDDLE:
+    case EL_BELT4_SWITCH_RIGHT:
       if (!player->Switching)
       {
        player->Switching = TRUE;
        ToggleBeltSwitch(x, y);
+       PlaySoundLevel(x, y, SND_CONVEYOR_BELT_SWITCH_ACTIVATING);
       }
       return MF_ACTION;
       break;
@@ -4921,6 +5952,7 @@ int DigField(struct PlayerInfo *player,
       {
        player->Switching = TRUE;
        ToggleSwitchgateSwitch(x, y);
+       PlaySoundLevel(x, y, SND_SWITCHGATE_SWITCH_ACTIVATING);
       }
       return MF_ACTION;
       break;
@@ -4931,24 +5963,54 @@ int DigField(struct PlayerInfo *player,
       {
        player->Switching = TRUE;
        ToggleLightSwitch(x, y);
+       PlaySoundLevel(x, y, element == EL_LIGHT_SWITCH_OFF ?
+                      SND_LIGHT_SWITCH_ACTIVATING :
+                      SND_LIGHT_SWITCH_DEACTIVATING);
       }
       return MF_ACTION;
       break;
 
+    case EL_TIMEGATE_SWITCH_OFF:
+      ActivateTimegateSwitch(x, y);
+      PlaySoundLevel(x, y, SND_TIMEGATE_WHEEL_ACTIVATING);
+
+      return MF_ACTION;
+      break;
+
+    case EL_BALLOON_SEND_LEFT:
+    case EL_BALLOON_SEND_RIGHT:
+    case EL_BALLOON_SEND_UP:
+    case EL_BALLOON_SEND_DOWN:
+    case EL_BALLOON_SEND_ANY:
+      if (element == EL_BALLOON_SEND_ANY)
+       game.balloon_dir = move_direction;
+      else
+       game.balloon_dir = (element == EL_BALLOON_SEND_LEFT  ? MV_LEFT :
+                           element == EL_BALLOON_SEND_RIGHT ? MV_RIGHT :
+                           element == EL_BALLOON_SEND_UP    ? MV_UP :
+                           element == EL_BALLOON_SEND_DOWN  ? MV_DOWN :
+                           MV_NO_MOVING);
+
+      return MF_ACTION;
+      break;
+
     case EL_SP_EXIT:
       if (local_player->gems_still_needed > 0)
        return MF_NO_ACTION;
 
       player->LevelSolved = player->GameOver = TRUE;
-      PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_SP_EXIT_ENTERING, SOUND_MAX_RIGHT);
       break;
 
     case EL_FELSBROCKEN:
+    case EL_BD_ROCK:
     case EL_BOMBE:
+    case EL_DX_SUPABOMB:
     case EL_KOKOSNUSS:
     case EL_ZEIT_LEER:
     case EL_SP_ZONK:
     case EL_SP_DISK_ORANGE:
+    case EL_SPRING:
       if (dy || mode == DF_SNAP)
        return MF_NO_ACTION;
 
@@ -4965,24 +6027,47 @@ int DigField(struct PlayerInfo *player,
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
+#if 0
       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-         !tape.playing)
+         !tape.playing && element != EL_SPRING)
        return MF_NO_ACTION;
+#else
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !(tape.playing && tape.game_version < GAME_VERSION_2_0) &&
+         element != EL_SPRING)
+       return MF_NO_ACTION;
+#endif
 
       RemoveField(x, y);
       Feld[x+dx][y+dy] = element;
 
-      player->push_delay_value = 2+RND(8);
+      if (element == EL_SPRING)
+      {
+       Feld[x+dx][y+dy] = EL_SPRING_MOVING;
+       MovDir[x+dx][y+dy] = move_direction;
+      }
+
+      player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
 
       DrawLevelField(x+dx, y+dy);
       if (element == EL_FELSBROCKEN)
-       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
+       PlaySoundLevel(x+dx, y+dy, SND_ROCK_PUSHING);
+      else if (element == EL_BD_ROCK)
+       PlaySoundLevel(x+dx, y+dy, SND_BD_ROCK_PUSHING);
+      else if (element == EL_BOMBE)
+       PlaySoundLevel(x+dx, y+dy, SND_BOMB_PUSHING);
+      else if (element == EL_DX_SUPABOMB)
+       PlaySoundLevel(x+dx, y+dy, SND_DX_BOMB_PUSHING);
       else if (element == EL_KOKOSNUSS)
-       PlaySoundLevel(x+dx, y+dy, SND_KNURK);
-      else if (IS_SP_ELEMENT(element))
-       PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
-      else
-       PlaySoundLevel(x+dx, y+dy, SND_KLOPF);
+       PlaySoundLevel(x+dx, y+dy, SND_NUT_PUSHING);
+      else if (element == EL_ZEIT_LEER)
+       PlaySoundLevel(x+dx, y+dy, SND_TIME_ORB_EMPTY_PUSHING);
+      else if (element == EL_SP_ZONK)
+       PlaySoundLevel(x+dx, y+dy, SND_SP_ZONK_PUSHING);
+      else if (element == EL_SP_DISK_ORANGE)
+       PlaySoundLevel(x+dx, y+dy, SND_SP_DISK_ORANGE_PUSHING);
+      else if (element == EL_SPRING)
+       PlaySoundLevel(x+dx, y+dy, SND_SPRING_PUSHING);
       break;
 
     case EL_PFORTE1:
@@ -5014,7 +6099,7 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
+      PlaySoundLevel(x, y, SND_GATE_PASSING);
 
       break;
 
@@ -5031,11 +6116,12 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
+      PlaySoundLevel(x, y, SND_GATE_PASSING);
 
       break;
 
     case EL_SWITCHGATE_OPEN:
+    case EL_TIMEGATE_OPEN:
       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
        return MF_NO_ACTION;
 
@@ -5043,7 +6129,10 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
+      if (element == EL_SWITCHGATE_OPEN)
+       PlaySoundLevel(x, y, SND_SWITCHGATE_PASSING);
+      else
+       PlaySoundLevel(x, y, SND_TIMEGATE_PASSING);
 
       break;
 
@@ -5086,8 +6175,48 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      PlaySoundLevel(x, y, SND_GATE);
+      PlaySoundLevel(x, y, SND_SP_PORT_PASSING);
+      break;
+
+    case EL_TUBE_CROSS:
+    case EL_TUBE_VERTICAL:
+    case EL_TUBE_HORIZONTAL:
+    case EL_TUBE_VERT_LEFT:
+    case EL_TUBE_VERT_RIGHT:
+    case EL_TUBE_HORIZ_UP:
+    case EL_TUBE_HORIZ_DOWN:
+    case EL_TUBE_LEFT_UP:
+    case EL_TUBE_LEFT_DOWN:
+    case EL_TUBE_RIGHT_UP:
+    case EL_TUBE_RIGHT_DOWN:
+      {
+       int i = 0;
+       int tube_enter_directions[][2] =
+       {
+         { EL_TUBE_CROSS,      MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
+         { EL_TUBE_VERTICAL,                        MV_UP | MV_DOWN },
+         { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT                   },
+         { EL_TUBE_VERT_LEFT,            MV_RIGHT | MV_UP | MV_DOWN },
+         { EL_TUBE_VERT_RIGHT, MV_LEFT            | MV_UP | MV_DOWN },
+         { EL_TUBE_HORIZ_UP,   MV_LEFT | MV_RIGHT |         MV_DOWN },
+         { EL_TUBE_HORIZ_DOWN, MV_LEFT | MV_RIGHT | MV_UP           },
+         { EL_TUBE_LEFT_UP,              MV_RIGHT |         MV_DOWN },
+         { EL_TUBE_LEFT_DOWN,            MV_RIGHT | MV_UP           },
+         { EL_TUBE_RIGHT_UP,   MV_LEFT |                    MV_DOWN },
+         { EL_TUBE_RIGHT_DOWN, MV_LEFT |            MV_UP           },
+         { -1,                 MV_NO_MOVING                         }
+       };
+
+       while (tube_enter_directions[i][0] != element)
+       {
+         i++;
+         if (tube_enter_directions[i][0] == -1)        /* should not happen */
+           break;
+       }
 
+       if (!(tube_enter_directions[i][1] & move_direction))
+         return MF_NO_ACTION;  /* tube has no opening in this direction */
+      }
       break;
 
     case EL_AUSGANG_ZU:
@@ -5100,7 +6229,7 @@ int DigField(struct PlayerInfo *player,
       if (mode == DF_SNAP)
        return MF_NO_ACTION;
 
-      PlaySoundLevel(x, y, SND_BUING);
+      PlaySoundLevel(x, y, SND_EXIT_ENTERING);
 
       break;
 
@@ -5108,7 +6237,7 @@ int DigField(struct PlayerInfo *player,
       Feld[x][y] = EL_BIRNE_EIN;
       local_player->lights_still_needed--;
       DrawLevelField(x, y);
-      PlaySoundLevel(x, y, SND_DENG);
+      PlaySoundLevel(x, y, SND_LAMP_ACTIVATING);
       return MF_ACTION;
       break;
 
@@ -5117,17 +6246,18 @@ int DigField(struct PlayerInfo *player,
       TimeLeft += 10;
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       DrawLevelField(x, y);
-      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MAX_RIGHT);
       return MF_ACTION;
       break;
 
     case EL_SOKOBAN_FELD_LEER:
       break;
 
-    case EL_SOKOBAN_FELD_VOLL:
     case EL_SOKOBAN_OBJEKT:
+    case EL_SOKOBAN_FELD_VOLL:
     case EL_SONDE:
     case EL_SP_DISK_YELLOW:
+    case EL_BALLOON:
       if (mode == DF_SNAP)
        return MF_NO_ACTION;
 
@@ -5152,9 +6282,16 @@ int DigField(struct PlayerInfo *player,
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
+#if 0
       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-         !tape.playing)
+         !tape.playing && element != EL_BALLOON)
        return MF_NO_ACTION;
+#else
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !(tape.playing && tape.game_version < GAME_VERSION_2_0) &&
+         element != EL_BALLOON)
+       return MF_NO_ACTION;
+#endif
 
       if (IS_SB_ELEMENT(element))
       {
@@ -5171,10 +6308,18 @@ int DigField(struct PlayerInfo *player,
          Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
          local_player->sokobanfields_still_needed--;
          if (element == EL_SOKOBAN_OBJEKT)
-           PlaySoundLevel(x, y, SND_DENG);
+           PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_FILLING);
+         else
+           PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
        }
        else
+       {
          Feld[x+dx][y+dy] = EL_SOKOBAN_OBJEKT;
+         if (element == EL_SOKOBAN_FELD_VOLL)
+           PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_CLEARING);
+         else
+           PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
+       }
       }
       else
       {
@@ -5182,23 +6327,27 @@ int DigField(struct PlayerInfo *player,
        Feld[x+dx][y+dy] = element;
       }
 
-      player->push_delay_value = 2;
+      player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
 
       DrawLevelField(x, y);
       DrawLevelField(x+dx, y+dy);
-      PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
+      if (element == EL_SONDE)
+       PlaySoundLevel(x+dx, y+dy, SND_SATELLITE_PUSHING);
+      else if (element == EL_SP_DISK_YELLOW)
+       PlaySoundLevel(x+dx, y+dy, SND_SP_DISK_YELLOW_PUSHING);
+      else if (element == EL_BALLOON)
+       PlaySoundLevel(x+dx, y+dy, SND_BALLOON_PUSHING);
 
       if (IS_SB_ELEMENT(element) &&
          local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
       {
        player->LevelSolved = player->GameOver = TRUE;
-       PlaySoundLevel(x, y, SND_BUING);
+       PlaySoundLevel(x, y, SND_SOKOBAN_GAME_SOLVING);
       }
 
       break;
 
-    case EL_MAULWURF:
     case EL_PINGUIN:
     case EL_SCHWEIN:
     case EL_DRACHE:
@@ -5279,6 +6428,8 @@ boolean PlaceBomb(struct PlayerInfo *player)
       else
        DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
     }
+
+    PlaySoundLevel(jx, jy, SND_DYNAMITE_PLACING);
   }
   else
   {
@@ -5287,47 +6438,44 @@ boolean PlaceBomb(struct PlayerInfo *player)
     player->dynabombs_left--;
     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNABOMB);
+
+    PlaySoundLevel(jx, jy, SND_DYNABOMB_PLACING);
   }
 
   return TRUE;
 }
 
-void PlaySoundLevel(int x, int y, int sound_nr)
+void PlaySoundLevel(int x, int y, int nr)
 {
   int sx = SCREENX(x), sy = SCREENY(y);
-  int volume, stereo;
-  int silence_distance = 8;
+  int volume, stereo_position;
+  int max_distance = 8;
+  int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
 
-  if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
-      (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
+  if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
+      (!setup.sound_loops && IS_LOOP_SOUND(nr)))
     return;
 
   if (!IN_LEV_FIELD(x, y) ||
-      sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
-      sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
+      sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
+      sy < -max_distance || sy >= SCR_FIELDY + max_distance)
     return;
 
-  volume = PSND_MAX_VOLUME;
-
-#ifndef MSDOS
-  stereo = (sx - SCR_FIELDX/2) * 12;
-#else
-  stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
-  if (stereo > PSND_MAX_RIGHT)
-    stereo = PSND_MAX_RIGHT;
-  if (stereo < PSND_MAX_LEFT)
-    stereo = PSND_MAX_LEFT;
-#endif
+  volume = SOUND_MAX_VOLUME;
 
   if (!IN_SCR_FIELD(sx, sy))
   {
-    int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
-    int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
+    int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
+    int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
 
-    volume -= volume * (dx > dy ? dx : dy) / silence_distance;
+    volume -= volume * (dx > dy ? dx : dy) / max_distance;
   }
 
-  PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
+  stereo_position = (SOUND_MAX_LEFT +
+                    (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
+                    (SCR_FIELDX + 2 * max_distance));
+
+  PlaySoundExt(nr, volume, stereo_position, type);
 }
 
 void RaiseScore(int value)
@@ -5383,6 +6531,31 @@ void RaiseScoreElement(int element)
   }
 }
 
+void RequestQuitGame(boolean ask_if_really_quit)
+{
+  if (AllPlayersGone ||
+      !ask_if_really_quit ||
+      level_editor_test_game ||
+      Request("Do you really want to quit the game ?",
+             REQ_ASK | REQ_STAY_CLOSED))
+  {
+#if defined(PLATFORM_UNIX)
+    if (options.network)
+      SendToServer_StopPlaying();
+    else
+#endif
+    {
+      game_status = MAINMENU;
+      DrawMainMenu();
+    }
+  }
+  else
+  {
+    OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+  }
+}
+
+
 /* ---------- new game button stuff ---------------------------------------- */
 
 /* graphic position values for game buttons */
@@ -5445,7 +6618,7 @@ void CreateGameButtons()
 
   for (i=0; i<NUM_GAME_BUTTONS; i++)
   {
-    Pixmap gd_pixmap = pix[PIX_DOOR];
+    Bitmap *gd_bitmap = pix[PIX_DOOR];
     struct GadgetInfo *gi;
     int button_type;
     boolean checked;
@@ -5490,10 +6663,10 @@ void CreateGameButtons()
                      GDI_TYPE, button_type,
                      GDI_STATE, GD_BUTTON_UNPRESSED,
                      GDI_CHECKED, checked,
-                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
-                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
-                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
-                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
+                     GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleGameButtons,
                      GDI_END);
@@ -5531,36 +6704,13 @@ static void HandleGameButtons(struct GadgetInfo *gi)
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
-      if (AllPlayersGone)
-      {
-       CloseDoor(DOOR_CLOSE_1);
-       game_status = MAINMENU;
-       DrawMainMenu();
-       break;
-      }
-
-      if (level_editor_test_game ||
-         Request("Do you really want to quit the game ?",
-                 REQ_ASK | REQ_STAY_CLOSED))
-      { 
-#ifndef MSDOS
-       if (options.network)
-         SendToServer_StopPlaying();
-       else
-#endif
-       {
-         game_status = MAINMENU;
-         DrawMainMenu();
-       }
-      }
-      else
-       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+      RequestQuitGame(TRUE);
       break;
 
     case GAME_CTRL_ID_PAUSE:
       if (options.network)
       {
-#ifndef MSDOS
+#if defined(PLATFORM_UNIX)
        if (tape.pausing)
          SendToServer_ContinuePlaying();
        else
@@ -5568,13 +6718,13 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 #endif
       }
       else
-       TapeTogglePause();
+       TapeTogglePause(TAPE_TOGGLE_MANUAL);
       break;
 
     case GAME_CTRL_ID_PLAY:
       if (tape.pausing)
       {
-#ifndef MSDOS
+#if defined(PLATFORM_UNIX)
        if (options.network)
          SendToServer_ContinuePlaying();
        else
@@ -5590,26 +6740,26 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       if (setup.sound_music)
       { 
        setup.sound_music = FALSE;
-       FadeSound(background_loop[level_nr % num_bg_loops]);
+       FadeMusic();
       }
-      else if (sound_loops_allowed)
+      else if (audio.music_available)
       { 
        setup.sound = setup.sound_music = TRUE;
-       PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
+       PlayMusic(level_nr);
       }
       break;
 
     case SOUND_CTRL_ID_LOOPS:
       if (setup.sound_loops)
        setup.sound_loops = FALSE;
-      else if (sound_loops_allowed)
+      else if (audio.loops_available)
        setup.sound = setup.sound_loops = TRUE;
       break;
 
     case SOUND_CTRL_ID_SIMPLE:
       if (setup.sound_simple)
        setup.sound_simple = FALSE;
-      else if (sound_status==SOUND_AVAILABLE)
+      else if (audio.sound_available)
        setup.sound = setup.sound_simple = TRUE;
       break;