rnd-19990925-1-src
[rocksndiamonds.git] / src / game.c
index d7fb2368f8536f59501a5dc05cbe86eb6309eda9..8c18d89ca64b71c9f1d92af961265137d20e4e37 100644 (file)
 #define NUM_GAME_BUTTONS               6
 
 /* forward declaration for internal use */
+static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
-static void KillHeroUnlessForceField(struct PlayerInfo *);
+static void KillHeroUnlessShield(struct PlayerInfo *);
 
 static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
@@ -408,7 +409,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:
@@ -480,8 +481,8 @@ void InitGame()
     player->last_jx = player->last_jy = 0;
     player->jx = player->jy = 0;
 
-    player->force_field_passive_time_left = 0;
-    player->force_field_active_time_left = 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);
@@ -515,8 +516,11 @@ void InitGame()
   AllPlayersGone = FALSE;
   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;
+
   for (i=0; i<4; i++)
   {
     game.belt_dir[i] = MV_NO_MOVING;
@@ -696,6 +700,11 @@ void InitGame()
   DrawAllPlayers();
   FadeToFront();
 
+  /* after drawing the level, corect some elements */
+
+  if (game.timegate_time_left == 0)
+    CloseAllOpenTimegates();
+
   if (setup.soft_scrolling)
     XCopyArea(display, fieldbuffer, backbuffer, gc,
              FX, FY, SXSIZE, SYSIZE, SX, SY);
@@ -886,7 +895,7 @@ void InitAmoebaNr(int x, int y)
 void GameWon()
 {
   int hi_pos;
-  int bumplevel = FALSE;
+  boolean raise_level = FALSE;
 
   if (local_player->MovPos)
     return;
@@ -957,17 +966,26 @@ void GameWon()
     SaveTape(tape.level_nr);           /* Ask to save tape */
   }
 
+  if (level_nr == leveldir[leveldir_nr].handicap_level)
+  {
+    leveldir[leveldir_nr].handicap_level++;
+    SaveLevelSetup_SeriesInfo(leveldir_nr);
+
+    if (level_nr < leveldir[leveldir_nr].last_level)
+      raise_level = TRUE;
+  }
+
   if ((hi_pos = NewHiScore()) >= 0) 
   {
     game_status = HALLOFFAME;
     DrawHallOfFame(hi_pos);
-    if (bumplevel && TAPE_IS_EMPTY(tape))
+    if (raise_level)
       level_nr++;
   }
   else
   {
     game_status = MAINMENU;
-    if (bumplevel && TAPE_IS_EMPTY(tape))
+    if (raise_level)
       level_nr++;
     DrawMainMenu();
   }
@@ -1257,7 +1275,7 @@ void Explode(int ex, int ey, int phase, int mode)
       if (IS_MASSIVE(element) || element == EL_BURNING)
        continue;
 
-      if (IS_PLAYER(x, y) && FORCE_FIELD_ON(PLAYERINFO(x, y)))
+      if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
       {
        if (IS_ACTIVE_BOMB(element))
        {
@@ -1272,7 +1290,7 @@ void Explode(int ex, int ey, int phase, int mode)
       if (element == EL_EXPLODING)
        element = Store2[x][y];
 
-      if (IS_PLAYER(ex, ey) && !FORCE_FIELD_ON(PLAYERINFO(ex, ey)))
+      if (IS_PLAYER(ex, ey) && !SHIELD_ON(PLAYERINFO(ex, ey)))
       {
        switch(StorePlayer[ex][ey])
        {
@@ -1373,7 +1391,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int element = Store2[x][y];
 
     if (IS_PLAYER(x, y))
-      KillHeroUnlessForceField(PLAYERINFO(x, y));
+      KillHeroUnlessShield(PLAYERINFO(x, y));
     else if (IS_EXPLOSIVE(element))
     {
       Feld[x][y] = Store2[x][y];
@@ -1650,39 +1668,79 @@ static void ToggleSwitchgateSwitch(int x, int y)
   }
 }
 
-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_OEFFNEN);
+      }
+
+      /*
+      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)
@@ -1720,7 +1778,7 @@ void Impact(int x, int y)
   if (element == EL_TROPFEN && (lastline || object_hit))       /* acid drop */
   {
     if (object_hit && IS_PLAYER(x, y+1))
-      KillHeroUnlessForceField(PLAYERINFO(x, y+1));
+      KillHeroUnlessShield(PLAYERINFO(x, y+1));
     else if (object_hit && smashed == EL_PINGUIN)
       Bang(x, y+1);
     else
@@ -1753,7 +1811,7 @@ void Impact(int x, int y)
 
     if (IS_PLAYER(x, y+1))
     {
-      KillHeroUnlessForceField(PLAYERINFO(x, y+1));
+      KillHeroUnlessShield(PLAYERINFO(x, y+1));
       return;
     }
     else if (smashed == EL_PINGUIN)
@@ -2156,6 +2214,11 @@ void TurnRound(int x, int y)
     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_ROBOT || element == EL_SONDE || element == EL_PINGUIN)
   {
     int attr_x = -1, attr_y = -1;
@@ -2460,7 +2523,8 @@ void StartMoving(int x, int y)
   {
     int newx, newy;
 
-    if (element == EL_SONDE && JustBeingPushed(x, y))
+    if ((element == EL_SONDE || element == EL_BALLOON)
+       && JustBeingPushed(x, y))
       return;
 
     if (!MovDelay[x][y])       /* start new movement phase */
@@ -2562,7 +2626,7 @@ void StartMoving(int x, int y)
     Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
 
     if (IS_ENEMY(element) && IS_PLAYER(newx, newy) &&
-       !FORCE_FIELD_ON(PLAYERINFO(newx, newy)))
+       !SHIELD_ON(PLAYERINFO(newx, newy)))
     {
 
 #if 1
@@ -2576,8 +2640,8 @@ void StartMoving(int x, int y)
 #endif
 
     }
-    else if ((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)
     {
@@ -2755,6 +2819,9 @@ 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))
+       TestIfBadThingHitsHero(x, y);
+
       return;
     }
 
@@ -3365,6 +3432,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_MIEP);
+      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 */
@@ -3521,6 +3613,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_OEFFNEN);
+      }
+    }
+  }
+}
+
 void EdelsteinFunkeln(int x, int y)
 {
   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
@@ -3910,7 +4069,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 ? 0 : 10);
 
        if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
        {
@@ -4092,6 +4252,8 @@ void GameActions()
       Life(x, y);
     else if (element == EL_ABLENK_EIN)
       Ablenk(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)
@@ -4125,12 +4287,16 @@ 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_PASSIVE)
-      DrawGraphicAnimation(x, y, GFX_FORCE_FIELD_PASSIVE, 6, 4, ANIM_NORMAL);
-    else if (element == EL_FORCE_FIELD_ACTIVE)
-      DrawGraphicAnimation(x, y, GFX_FORCE_FIELD_ACTIVE, 6, 4, ANIM_NORMAL);
+    else if (element == EL_SHIELD_PASSIVE)
+      DrawGraphicAnimation(x, y, GFX_SHIELD_PASSIVE, 6, 4, ANIM_NORMAL);
+    else if (element == EL_SHIELD_ACTIVE)
+      DrawGraphicAnimation(x, y, GFX_SHIELD_ACTIVE, 6, 4, ANIM_NORMAL);
 
     if (game.magic_wall_active)
     {
@@ -4213,6 +4379,14 @@ void GameActions()
     }
   }
 
+  if (game.timegate_time_left > 0)
+  {
+    game.timegate_time_left--;
+
+    if (game.timegate_time_left == 0)
+      CloseAllOpenTimegates();
+  }
+
   if (TimeFrames >= (1000 / GameFrameDelay))
   {
     TimeFrames = 0;
@@ -4220,12 +4394,12 @@ void GameActions()
 
     for (i=0; i<MAX_PLAYERS; i++)
     {
-      if (FORCE_FIELD_ON(&stored_player[i]))
+      if (SHIELD_ON(&stored_player[i]))
       {
-       stored_player[i].force_field_passive_time_left--;
+       stored_player[i].shield_passive_time_left--;
 
-       if (stored_player[i].force_field_active_time_left > 0)
-         stored_player[i].force_field_active_time_left--;
+       if (stored_player[i].shield_active_time_left > 0)
+         stored_player[i].shield_active_time_left--;
       }
     }
 
@@ -4236,12 +4410,12 @@ void GameActions()
     {
       TimeLeft--;
 
-      if (TimeLeft <= 10)
+      if (TimeLeft <= 10 && setup.time_limit)
        PlaySoundStereo(SND_GONG, PSND_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]);
     }
@@ -4390,7 +4564,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
 #if 1
       TestIfBadThingHitsHero(new_jx, new_jy);
 #else
-      if (player->force_field_time_left == 0)
+      if (player->shield_time_left == 0)
        KillHero(player);
 #endif
     }
@@ -4710,9 +4884,9 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
     {
       struct PlayerInfo *player = PLAYERINFO(goodx, goody);
 
-      if (player->force_field_active_time_left > 0)
+      if (player->shield_active_time_left > 0)
        Bang(killx, killy);
-      else if (player->force_field_passive_time_left == 0)
+      else if (player->shield_passive_time_left == 0)
        KillHero(player);
     }
     else
@@ -4738,6 +4912,9 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     MV_DOWN
   };
 
+  if (Feld[badx][bady] == EL_EXPLODING)        /* skip just exploding bad things */
+    return;
+
   for (i=0; i<4; i++)
   {
     int x, y, element;
@@ -4772,9 +4949,9 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     {
       struct PlayerInfo *player = PLAYERINFO(killx, killy);
 
-      if (player->force_field_active_time_left > 0)
+      if (player->shield_active_time_left > 0)
        Bang(badx, bady);
-      else if (player->force_field_passive_time_left == 0)
+      else if (player->shield_passive_time_left == 0)
        KillHero(player);
     }
     else
@@ -4846,17 +5023,17 @@ void KillHero(struct PlayerInfo *player)
   if (IS_PFORTE(Feld[jx][jy]))
     Feld[jx][jy] = EL_LEERRAUM;
 
-  /* deactivate force field (else Bang()/Explode() would not work right) */
-  player->force_field_passive_time_left = 0;
-  player->force_field_active_time_left = 0;
+  /* 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 KillHeroUnlessForceField(struct PlayerInfo *player)
+static void KillHeroUnlessShield(struct PlayerInfo *player)
 {
-  if (!FORCE_FIELD_ON(player))
+  if (!SHIELD_ON(player))
     KillHero(player);
 }
 
@@ -4981,16 +5158,16 @@ int DigField(struct PlayerInfo *player,
       PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
       break;
 
-    case EL_FORCE_FIELD_PASSIVE:
+    case EL_SHIELD_PASSIVE:
       RemoveField(x, y);
-      player->force_field_passive_time_left += 10;
+      player->shield_passive_time_left += 10;
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
-    case EL_FORCE_FIELD_ACTIVE:
+    case EL_SHIELD_ACTIVE:
       RemoveField(x, y);
-      player->force_field_passive_time_left += 10;
-      player->force_field_active_time_left += 10;
+      player->shield_passive_time_left += 10;
+      player->shield_active_time_left += 10;
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
@@ -5137,6 +5314,29 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
+    case EL_TIMEGATE_SWITCH_OFF:
+      ActivateTimegateSwitch(x, y);
+
+      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;
@@ -5184,7 +5384,7 @@ int DigField(struct PlayerInfo *player,
       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_PUSCH);  /* better than "SND_KLOPF" */
       break;
 
     case EL_PFORTE1:
@@ -5238,6 +5438,7 @@ int DigField(struct PlayerInfo *player,
       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;
 
@@ -5330,6 +5531,7 @@ int DigField(struct PlayerInfo *player,
     case EL_SOKOBAN_OBJEKT:
     case EL_SONDE:
     case EL_SP_DISK_YELLOW:
+    case EL_BALLOON:
       if (mode == DF_SNAP)
        return MF_NO_ACTION;
 
@@ -5355,7 +5557,7 @@ int DigField(struct PlayerInfo *player,
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-         !tape.playing)
+         !tape.playing && element != EL_BALLOON)
        return MF_NO_ACTION;
 
       if (IS_SB_ELEMENT(element))
@@ -5384,11 +5586,14 @@ 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_BALLOON)
+       PlaySoundLevel(x+dx, y+dy, SND_SCHLURF);
+      else
+       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
 
       if (IS_SB_ELEMENT(element) &&
          local_player->sokobanfields_still_needed == 0 &&