rnd-20020527-2-src
[rocksndiamonds.git] / src / game.c
index 63c05b56dcab7783d9b652599623f09358c4eeda..cda2f48694df03de750c8c7ea841eb95045bcae1 100644 (file)
 #define DX_TIME                        (DX + XX_TIME)
 #define DY_TIME                        (DY + YY_TIME)
 
-#define IS_LOOP_SOUND(s)       ((s) == SND_BD_MAGIC_WALL_RUNNING ||    \
-                                (s) == SND_BD_BUTTERFLY_MOVING ||      \
-                                (s) == SND_BD_FIREFLY_MOVING ||        \
-                                (s) == SND_SP_SNIKSNAK_MOVING ||       \
-                                (s) == SND_SP_ELECTRON_MOVING ||       \
-                                (s) == SND_DYNAMITE_BURNING || \
-                                (s) == SND_BUG_MOVING ||       \
-                                (s) == SND_SPACESHIP_MOVING || \
-                                (s) == SND_YAMYAM_MOVING ||    \
-                                (s) == SND_YAMYAM_WAITING ||   \
-                                (s) == SND_ROBOT_WHEEL_RUNNING ||      \
-                                (s) == SND_MAGIC_WALL_RUNNING ||       \
-                                (s) == SND_BALLOON_MOVING ||   \
-                                (s) == SND_MOLE_MOVING ||      \
-                                (s) == SND_TIMEGATE_WHEEL_RUNNING ||   \
-                                (s) == SND_CONVEYOR_BELT_RUNNING ||    \
-                                (s) == SND_DYNABOMB_BURNING || \
-                                (s) == SND_PACMAN_MOVING ||    \
-                                (s) == SND_PENGUIN_MOVING ||   \
-                                (s) == SND_PIG_MOVING ||       \
-                                (s) == SND_DRAGON_MOVING ||    \
-                                (s) == SND_DRAGON_BREATHING_FIRE)
-
 /* values for player movement speed (which is in fact a delay value) */
 #define MOVE_DELAY_NORMAL_SPEED        8
 #define MOVE_DELAY_HIGH_SPEED  4
@@ -123,11 +100,57 @@ static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void KillHeroUnlessProtected(int, int);
 
+void PlaySoundLevel(int, int, int);
+void PlaySoundLevelAction(int, int, int);
+void PlaySoundLevelActionElement(int, int, int, int);
+
 static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
+#define SND_ACTION_UNKNOWN             0
+#define SND_ACTION_WAITING             1
+#define SND_ACTION_MOVING              2
+#define SND_ACTION_DIGGING             3
+#define SND_ACTION_COLLECTING          4
+#define SND_ACTION_PASSING             5
+#define SND_ACTION_IMPACT              6
+#define SND_ACTION_PUSHING             7
+#define SND_ACTION_ACTIVATING          8
+#define SND_ACTION_BURNING             9
+
+#define NUM_SND_ACTIONS                        10
+
+static struct
+{
+  char *text;
+  int value;
+  boolean is_loop;
+} sound_action_properties[] =
+{
+  /* insert _all_ loop sound actions here */
+  { ".waiting",                SND_ACTION_WAITING,     TRUE },
+  { ".moving",         SND_ACTION_MOVING,      TRUE }, /* continuos moving */
+  { ".running",                SND_ACTION_UNKNOWN,     TRUE },
+  { ".burning",                SND_ACTION_BURNING,     TRUE },
+  { ".growing",                SND_ACTION_UNKNOWN,     TRUE },
+  { ".attacking",      SND_ACTION_UNKNOWN,     TRUE },
+
+  /* other (non-loop) sound actions are optional */
+  { ".stepping",       SND_ACTION_MOVING,      FALSE }, /* discrete moving */
+  { ".digging",                SND_ACTION_DIGGING,     FALSE },
+  { ".collecting",     SND_ACTION_COLLECTING,  FALSE },
+  { ".passing",                SND_ACTION_PASSING,     FALSE },
+  { ".impact",         SND_ACTION_IMPACT,      FALSE },
+  { ".pushing",                SND_ACTION_PUSHING,     FALSE },
+  { ".activating",     SND_ACTION_ACTIVATING,  FALSE },
+  { NULL,              0,                      0 },
+};
+static int element_action_sound[NUM_LEVEL_ELEMENTS][NUM_SND_ACTIONS];
+static boolean is_loop_sound[NUM_SOUND_EFFECTS];
+
+#define IS_LOOP_SOUND(x)       (is_loop_sound[x])
 
 
 #ifdef DEBUG
@@ -468,6 +491,90 @@ void DrawGameDoorValues()
           int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 }
 
+void InitGameEngine()
+{
+  static int sound_effect_properties[NUM_SOUND_EFFECTS];
+  int i, j;
+
+#if 0
+  debug_print_timestamp(0, NULL);
+#endif
+
+  for (i=0; i<NUM_SND_ACTIONS; i++)
+    for (j=0; j<NUM_LEVEL_ELEMENTS; j++)
+      element_action_sound[j][i] = -1;
+
+  for (i=0; i<NUM_SOUND_EFFECTS; i++)
+  {
+    int len_effect_text = strlen(sound_effects[i].text);
+
+    sound_effect_properties[i] = SND_ACTION_UNKNOWN;
+    is_loop_sound[i] = FALSE;
+
+    /* determine all loop sounds and identify certain sound classes */
+
+    j = 0;
+    while (sound_action_properties[j].text)
+    {
+      int len_action_text = strlen(sound_action_properties[j].text);
+
+      if (len_action_text < len_effect_text &&
+         strcmp(&sound_effects[i].text[len_effect_text - len_action_text],
+                sound_action_properties[j].text) == 0)
+      {
+       sound_effect_properties[i] = sound_action_properties[j].value;
+
+       if (sound_action_properties[j].is_loop)
+         is_loop_sound[i] = TRUE;
+      }
+
+      j++;
+    }
+
+    /* associate elements and some selected sound actions */
+
+    for (j=0; j<NUM_LEVEL_ELEMENTS; j++)
+    {
+      if (element_info[j].sound_class_name)
+      {
+       int len_class_text = strlen(element_info[j].sound_class_name);
+
+       if (len_class_text + 1 < len_effect_text &&
+           strncmp(sound_effects[i].text,
+                   element_info[j].sound_class_name, len_class_text) == 0 &&
+           sound_effects[i].text[len_class_text] == '.')
+       {
+         int sound_action_value = sound_effect_properties[i];
+
+         element_action_sound[j][sound_action_value] = i;
+       }
+      }
+    }
+  }
+
+#if 0
+  debug_print_timestamp(0, "InitGameEngine");
+#endif
+
+#if 0
+  /* TEST ONLY */
+  {
+    int element = EL_ERDREICH;
+    int sound_action = SND_ACTION_DIGGING;
+    int j = 0;
+
+    while (sound_action_properties[j].text)
+    {
+      if (sound_action_properties[j].value == sound_action)
+       printf("element %d, sound action '%s'  == %d\n",
+              element, sound_action_properties[j].text,
+              element_action_sound[element][sound_action]);
+      j++;
+    }
+  }
+#endif
+}
+
 void InitGame()
 {
   int i, j, x, y;
@@ -844,7 +951,7 @@ void InitGame()
 
   OpenDoor(DOOR_OPEN_ALL);
 
-  PlaySoundStereo(SND_GAME_STARTING, PSND_MAX_RIGHT);
+  PlaySoundStereo(SND_GAME_STARTING, SOUND_MAX_RIGHT);
   if (setup.sound_music)
     PlayMusic(level_nr);
 
@@ -997,18 +1104,18 @@ void GameWon()
 
   local_player->LevelSolved = FALSE;
 
-  PlaySoundStereo(SND_GAME_WINNING, PSND_MAX_RIGHT);
+  PlaySoundStereo(SND_GAME_WINNING, SOUND_MAX_RIGHT);
 
   if (TimeLeft)
   {
     if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, PSND_MAX_VOLUME, PSND_MAX_RIGHT,
-                  PSND_LOOP);
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+                  SND_CTRL_PLAY_LOOP);
 
     while (TimeLeft > 0)
     {
       if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, PSND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
       if (TimeLeft > 0 && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
@@ -1028,13 +1135,13 @@ void GameWon()
   else if (level.time == 0)            /* level without time limit */
   {
     if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, PSND_MAX_VOLUME, PSND_MAX_RIGHT,
-                  PSND_LOOP);
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+                  SND_CTRL_PLAY_LOOP);
 
     while (TimePlayed < 999)
     {
       if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, PSND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
       if (TimePlayed < 999 && !(TimePlayed % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimePlayed < 900 && !(TimePlayed % 10))
@@ -1328,13 +1435,8 @@ void CheckDynamite(int x, int y)
     MovDelay[x][y]--;
     if (MovDelay[x][y])
     {
-      if (!(MovDelay[x][y] % 12))
-      {
-       if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
-         PlaySoundLevel(x, y, SND_DYNAMITE_BURNING);
-       else
-         PlaySoundLevel(x, y, SND_DYNABOMB_BURNING);
-      }
+      if (!(MovDelay[x][y] % 6))
+       PlaySoundLevelAction(x, y, SND_ACTION_BURNING);
 
       if (IS_ACTIVE_BOMB(Feld[x][y]))
       {
@@ -2060,61 +2162,7 @@ void Impact(int x, int y)
 
   /* play sound of object that hits the ground */
   if (lastline || object_hit)
-  {
-    int sound;
-
-    switch (element)
-    {
-      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_SP_INFOTRON_IMPACT;
-       break;
-      case EL_KOKOSNUSS:
-       sound = SND_NUT_IMPACT;
-       break;
-      case EL_BD_ROCK:
-       sound = SND_BD_ROCK_IMPACT;
-       break;
-      case EL_FELSBROCKEN:
-       sound = SND_ROCK_IMPACT;
-       break;
-      case EL_SP_ZONK:
-       sound = SND_SP_ZONK_IMPACT;
-       break;
-      case EL_ZEIT_VOLL:
-       sound = SND_TIME_ORB_FULL_IMPACT;
-       break;
-      case EL_ZEIT_LEER:
-       sound = SND_TIME_ORB_EMPTY_IMPACT;
-       break;
-      case EL_SPRING:
-       sound = SND_SPRING_IMPACT;
-       break;
-      default:
-       sound = -1;
-        break;
-    }
-
-    if (sound >= 0)
-      PlaySoundLevel(x, y, sound);
-  }
+    PlaySoundLevelActionElement(x, y, SND_ACTION_IMPACT, element);
 }
 
 void TurnRound(int x, int y)
@@ -2752,6 +2800,7 @@ 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 ||
                               element == EL_SP_SNIKSNAK ||
@@ -2829,43 +2878,14 @@ void StartMoving(int x, int y)
        }
       }
 
-      if (MovDelay[x][y])
+      if (MovDelay[x][y])      /* element still has to wait some time */
+      {
+       PlaySoundLevelAction(x, y, SND_ACTION_WAITING);
+
        return;
+      }
     }
 
-    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 */
@@ -2980,7 +3000,7 @@ void StartMoving(int x, int y)
          else
            DrawLevelField(x, y);
 
-         PlaySoundLevel(x, y, SND_DRAGON_BREATHING_FIRE);
+         PlaySoundLevel(x, y, SND_DRAGON_ATTACKING);
 
          MovDelay[x][y] = 50;
          Feld[newx][newy] = EL_BURNING;
@@ -3077,13 +3097,14 @@ void StartMoving(int x, int y)
       if (DONT_TOUCH(element))
        TestIfBadThingTouchesHero(x, y);
 
+      PlaySoundLevelAction(x, y, SND_ACTION_WAITING);
+
       return;
     }
 
-    if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
-      PlaySoundLevel(x, y, SND_ROBOT_MOVING);
-
     InitMovingField(x, y, MovDir[x][y]);
+
+    PlaySoundLevelAction(x, y, SND_ACTION_MOVING);
   }
 
   if (MovDir[x][y])
@@ -3123,7 +3144,7 @@ void ContinueMoving(int x, int y)
 
   MovPos[x][y] += step;
 
-  if (ABS(MovPos[x][y])>=TILEX)                /* object reached its destination */
+  if (ABS(MovPos[x][y]) >= TILEX)      /* object reached its destination */
   {
     Feld[x][y] = EL_LEERRAUM;
     Feld[newx][newy] = element;
@@ -4358,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);
   }
 }
 
@@ -4708,11 +4732,7 @@ void GameActions()
     else if (element == EL_SP_TERMINAL_ACTIVE)
       DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
     else if (IS_BELT(element))
-    {
       DrawBeltAnimation(x, y, element);
-      if (!(FrameCounter % 2))
-       PlaySoundLevel(x, y, SND_CONVEYOR_BELT_RUNNING);
-    }
     else if (element == EL_SWITCHGATE_OPENING)
       OpenSwitchgate(x, y);
     else if (element == EL_SWITCHGATE_CLOSING)
@@ -4921,7 +4941,7 @@ void GameActions()
       TimeLeft--;
 
       if (TimeLeft <= 10 && setup.time_limit)
-       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, PSND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MAX_RIGHT);
 
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
@@ -5044,7 +5064,9 @@ static void CheckGravityMovement(struct PlayerInfo *player)
        (Feld[new_jx][new_jy] == EL_SP_BASE ||
        Feld[new_jx][new_jy] == EL_ERDREICH));
 
-    if (field_under_player_is_free && !player_is_moving_to_valid_field)
+    if (field_under_player_is_free &&
+       !player_is_moving_to_valid_field &&
+       !IS_TUBE(Feld[jx][jy]))
       player->programmed_action = MV_DOWN;
   }
 }
@@ -5666,7 +5688,7 @@ int DigField(struct PlayerInfo *player,
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
-  if (!player->MovPos)
+  if (player->MovPos == 0)
     player->Pushing = FALSE;
 
   if (mode == DF_NO_PUSH)
@@ -5720,19 +5742,7 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_BASE:
     case EL_SP_BUG:
       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);
+      PlaySoundLevelActionElement(x, y, SND_ACTION_DIGGING, element);
       break;
 
     case EL_EDELSTEIN:
@@ -5754,19 +5764,7 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3),
               FS_SMALL, FC_YELLOW);
-
-      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);
+      PlaySoundLevelActionElement(x, y, SND_ACTION_COLLECTING, element);
       break;
 
     case EL_SPEED_PILL:
@@ -5787,7 +5785,7 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       }
-      PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MAX_RIGHT);
       break;
 
     case EL_SHIELD_PASSIVE:
@@ -5811,10 +5809,7 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_DYNAMITE, DY_DYNAMITE,
               int2str(local_player->dynamite, 3),
               FS_SMALL, FC_YELLOW);
-      if (element == EL_SP_DISK_RED)
-       PlaySoundLevel(x, y, SND_SP_DISK_RED_COLLECTING);
-      else
-       PlaySoundLevel(x, y, SND_DYNAMITE_COLLECTING);
+      PlaySoundLevelActionElement(x, y, SND_ACTION_COLLECTING, element);
       break;
 
     case EL_DYNABOMB_NR:
@@ -5970,6 +5965,7 @@ int DigField(struct PlayerInfo *player,
                            element == EL_BALLOON_SEND_UP    ? MV_UP :
                            element == EL_BALLOON_SEND_DOWN  ? MV_DOWN :
                            MV_NO_MOVING);
+      PlaySoundLevel(x, y, SND_BALLOON_SWITCH_ACTIVATING);
 
       return MF_ACTION;
       break;
@@ -5979,11 +5975,11 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
 
       player->LevelSolved = player->GameOver = TRUE;
-      PlaySoundStereo(SND_SP_EXIT_ENTERING, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_SP_EXIT_ENTERING, SOUND_MAX_RIGHT);
       break;
 
+      /* the following elements cannot be pushed by "snapping" */
     case EL_FELSBROCKEN:
-    case EL_BD_ROCK:
     case EL_BOMBE:
     case EL_DX_SUPABOMB:
     case EL_KOKOSNUSS:
@@ -5991,7 +5987,12 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_ZONK:
     case EL_SP_DISK_ORANGE:
     case EL_SPRING:
-      if (dy || mode == DF_SNAP)
+      if (mode == DF_SNAP)
+       return MF_NO_ACTION;
+      /* no "break" -- fall through to next case */
+      /* the following elements can be pushed by "snapping" */
+    case EL_BD_ROCK:
+      if (dy)
        return MF_NO_ACTION;
 
       player->Pushing = TRUE;
@@ -6018,8 +6019,16 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
 #endif
 
-      RemoveField(x, y);
-      Feld[x+dx][y+dy] = element;
+      if (mode == DF_SNAP)
+      {
+       InitMovingField(x, y, move_direction);
+       ContinueMoving(x, y);
+      }
+      else
+      {
+       RemoveField(x, y);
+       Feld[x+dx][y+dy] = element;
+      }
 
       if (element == EL_SPRING)
       {
@@ -6030,24 +6039,7 @@ int DigField(struct PlayerInfo *player,
       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_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_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);
+      PlaySoundLevelActionElement(x, y, SND_ACTION_PUSHING, element);
       break;
 
     case EL_PFORTE1:
@@ -6080,7 +6072,6 @@ int DigField(struct PlayerInfo *player,
       DOUBLE_PLAYER_SPEED(player);
 
       PlaySoundLevel(x, y, SND_GATE_PASSING);
-
       break;
 
     case EL_EM_GATE_1X:
@@ -6097,7 +6088,6 @@ int DigField(struct PlayerInfo *player,
       DOUBLE_PLAYER_SPEED(player);
 
       PlaySoundLevel(x, y, SND_GATE_PASSING);
-
       break;
 
     case EL_SWITCHGATE_OPEN:
@@ -6109,11 +6099,7 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
-      if (element == EL_SWITCHGATE_OPEN)
-       PlaySoundLevel(x, y, SND_SWITCHGATE_PASSING);
-      else
-       PlaySoundLevel(x, y, SND_TIMEGATE_PASSING);
-
+      PlaySoundLevelActionElement(x, y, SND_ACTION_PASSING, element);
       break;
 
     case EL_SP_PORT1_LEFT:
@@ -6196,6 +6182,8 @@ int DigField(struct PlayerInfo *player,
 
        if (!(tube_enter_directions[i][1] & move_direction))
          return MF_NO_ACTION;  /* tube has no opening in this direction */
+
+       PlaySoundLevel(x, y, SND_TUBE_PASSING);
       }
       break;
 
@@ -6226,7 +6214,7 @@ int DigField(struct PlayerInfo *player,
       TimeLeft += 10;
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       DrawLevelField(x, y);
-      PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, PSND_MAX_RIGHT);
+      PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MAX_RIGHT);
       return MF_ACTION;
       break;
 
@@ -6305,18 +6293,13 @@ int DigField(struct PlayerInfo *player,
       {
        RemoveField(x, y);
        Feld[x+dx][y+dy] = element;
+       PlaySoundLevelActionElement(x, y, SND_ACTION_PUSHING, element);
       }
 
       player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
 
       DrawLevelField(x, y);
       DrawLevelField(x+dx, y+dy);
-      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 &&
@@ -6355,6 +6338,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   if (!dx && !dy)
   {
+    if (player->MovPos == 0)
+      player->Pushing = FALSE;
+
     player->snapped = FALSE;
     return FALSE;
   }
@@ -6425,42 +6411,64 @@ boolean PlaceBomb(struct PlayerInfo *player)
   return TRUE;
 }
 
-void PlaySoundLevel(int x, int y, int sound_nr)
+void PlaySoundLevel(int x, int y, int nr)
 {
+  static int loop_sound_frame[NUM_SOUND_EFFECTS];
+  static int loop_sound_volume[NUM_SOUND_EFFECTS];
   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;
-
-#if !defined(PLATFORM_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) / max_distance;
+  }
+
+  stereo_position = (SOUND_MAX_LEFT +
+                    (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
+                    (SCR_FIELDX + 2 * max_distance));
+
+  if (IS_LOOP_SOUND(nr))
+  {
+    /* This assures that quieter loop sounds do not overwrite louder ones,
+       while restarting sound volume comparison with each new game frame. */
+
+    if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
+      return;
 
-    volume -= volume * (dx > dy ? dx : dy) / silence_distance;
+    loop_sound_volume[nr] = volume;
+    loop_sound_frame[nr] = FrameCounter;
   }
 
-  PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
+  PlaySoundExt(nr, volume, stereo_position, type);
+}
+
+void PlaySoundLevelAction(int x, int y, int sound_action)
+{
+  PlaySoundLevelActionElement(x, y, sound_action, Feld[x][y]);
+}
+
+void PlaySoundLevelActionElement(int x, int y, int sound_action, int element)
+{
+  int sound_effect = element_action_sound[element][sound_action];
+
+  if (sound_effect != -1)
+    PlaySoundLevel(x, y, sound_effect);
 }
 
 void RaiseScore(int value)