rnd-20030422-1-src
[rocksndiamonds.git] / src / game.c
index fb21d6f62cafab430c342d9c7f88b9ee7473a34f..1736366565c50950c217301519f37d01f8a42ec1 100644 (file)
@@ -533,21 +533,15 @@ static void InitGameEngine()
     };
     static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
 
+    /* special EM style gems behaviour */
     for (i=0; i<ep_em_slippery_wall_num; i++)
-    {
-      if (level.em_slippery_gems)      /* special EM style gems behaviour */
-       Properties2[ep_em_slippery_wall[i]] |=
-         EP_BIT_EM_SLIPPERY_WALL;
-      else
-       Properties2[ep_em_slippery_wall[i]] &=
-         ~EP_BIT_EM_SLIPPERY_WALL;
-    }
+      SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
+                  level.em_slippery_gems);
 
     /* "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1 */
-    if (level.em_slippery_gems && game.engine_version > VERSION_IDENT(2,0,1))
-      Properties2[EL_EXPANDABLE_WALL_GROWING] |= EP_BIT_EM_SLIPPERY_WALL;
-    else
-      Properties2[EL_EXPANDABLE_WALL_GROWING] &=~EP_BIT_EM_SLIPPERY_WALL;
+    SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
+                (level.em_slippery_gems &&
+                 game.engine_version > VERSION_IDENT(2,0,1)));
   }
 
   /* initialize changing elements information */
@@ -643,9 +637,10 @@ void InitGame()
     player->Pushing = FALSE;
     player->Switching = FALSE;
     player->GfxPos = 0;
-    player->Frame = 0;
-
+    player->GfxDir = MV_NO_MOVING;
     player->GfxAction = ACTION_DEFAULT;
+    player->Frame = 0;
+    player->StepFrame = 0;
 
     player->use_murphy_graphic = FALSE;
     player->use_disk_red_graphic = FALSE;
@@ -657,6 +652,8 @@ void InitGame()
 
     player->is_moving = FALSE;
     player->is_waiting = FALSE;
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
 
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
@@ -726,7 +723,7 @@ void InitGame()
     {
       Feld[x][y] = Ur[x][y];
       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
-      Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
+      Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
       AmoebaNr[x][y] = 0;
       JustStopped[x][y] = 0;
       Stop[x][y] = FALSE;
@@ -916,7 +913,7 @@ void InitGame()
   else
   {
     DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
-               int2str(level_nr, 3), FONT_LEVEL_NUMBER, FONT_OPAQUE);
+               int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
     BlitBitmap(drawto, drawto,
               DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
               getFontWidth(FONT_LEVEL_NUMBER) * 3,
@@ -940,11 +937,12 @@ void InitGame()
 
   OpenDoor(DOOR_OPEN_ALL);
 
-  PlaySoundStereo(SND_GAME_STARTING, SOUND_MAX_RIGHT);
+  PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
   if (setup.sound_music)
     PlayMusic(level_nr);
 
-  KeyboardAutoRepeatOff();
+  if (!tape.playing)
+    KeyboardAutoRepeatOff();
 
   if (options.debug)
   {
@@ -1096,20 +1094,20 @@ void GameWon()
 
   local_player->LevelSolved = FALSE;
 
-  PlaySoundStereo(SND_GAME_WINNING, SOUND_MAX_RIGHT);
+  PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
 
   if (TimeLeft)
   {
     if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
                   SND_CTRL_PLAY_LOOP);
 
     while (TimeLeft > 0)
     {
       if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
       if (TimeLeft > 0 && !(TimeLeft % 10))
-       RaiseScore(level.score[SC_ZEITBONUS]);
+       RaiseScore(level.score[SC_TIME_BONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
        TimeLeft -= 10;
       else
@@ -1127,15 +1125,15 @@ void GameWon()
   else if (level.time == 0)            /* level without time limit */
   {
     if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MAX_RIGHT,
+      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
                   SND_CTRL_PLAY_LOOP);
 
     while (TimePlayed < 999)
     {
       if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
       if (TimePlayed < 999 && !(TimePlayed % 10))
-       RaiseScore(level.score[SC_ZEITBONUS]);
+       RaiseScore(level.score[SC_TIME_BONUS]);
       if (TimePlayed < 900 && !(TimePlayed % 10))
        TimePlayed += 10;
       else
@@ -1261,12 +1259,19 @@ int NewHiScore()
   return position;
 }
 
-static void InitPlayerGfxAnimation(struct PlayerInfo *player, int action)
+void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
 {
-  if (player->GfxAction != action)
+  if (player->GfxAction != action || player->GfxDir != dir)
   {
+#if 0
+    printf("Player frame reset! (%d => %d, %d => %d)\n",
+          player->GfxAction, action, player->GfxDir, dir);
+#endif
+
     player->GfxAction = action;
+    player->GfxDir = dir;
     player->Frame = 0;
+    player->StepFrame = 0;
   }
 }
 
@@ -1374,6 +1379,7 @@ static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
 static void RemoveField(int x, int y)
 {
   Feld[x][y] = EL_EMPTY;
+  GfxElement[x][y] = EL_UNDEFINED;
   MovPos[x][y] = 0;
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
@@ -1428,13 +1434,18 @@ void DrawDynamite(int x, int y)
   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
     return;
 
-  if (Store[x][y])
+  if (IS_WALKABLE_INSIDE(Back[x][y]))
+    return;
+
+  if (Back[x][y])
+    DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
+  else if (Store[x][y])
     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
 
   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
 #if 1
-  if (Store[x][y])
+  if (Back[x][y] || Store[x][y])
     DrawGraphicThruMask(sx, sy, graphic, frame);
   else
     DrawGraphic(sx, sy, graphic, frame);
@@ -1495,6 +1506,10 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int center_element = Feld[ex][ey];
 
+    /* remove things displayed in background while burning dynamite */
+    if (!IS_INDESTRUCTIBLE(Back[ex][ey]))
+      Back[ex][ey] = 0;
+
     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
     {
       /* put moving element to center field (and let it explode there) */
@@ -1520,7 +1535,10 @@ void Explode(int ex, int ey, int phase, int mode)
        RemoveMovingField(x, y);
       }
 
-      if (IS_INDESTRUCTIBLE(element) || element == EL_FLAMES)
+      if ((IS_INDESTRUCTIBLE(element) &&
+          (game.engine_version < VERSION_IDENT(2,2,0) ||
+           (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
+         element == EL_FLAMES)
        continue;
 
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
@@ -1535,6 +1553,11 @@ void Explode(int ex, int ey, int phase, int mode)
        continue;
       }
 
+      /* save walkable background elements while explosion on same tile */
+      if (IS_INDESTRUCTIBLE(element))
+       Back[x][y] = element;
+
+      /* ignite explodable elements reached by other explosion */
       if (element == EL_EXPLOSION)
        element = Store2[x][y];
 
@@ -1590,7 +1613,7 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_PEARL;
       else if (element == EL_WALL_CRYSTAL)
        Store[x][y] = EL_CRYSTAL;
-      else if (!IS_PFORTE(Store[x][y]))
+      else
        Store[x][y] = EL_EMPTY;
 
       if (x != ex || y != ey ||
@@ -1607,6 +1630,7 @@ void Explode(int ex, int ey, int phase, int mode)
       }
 
       Feld[x][y] = EL_EXPLOSION;
+      GfxElement[x][y] = EL_UNDEFINED;
       MovDir[x][y] = MovPos[x][y] = 0;
       AmoebaNr[x][y] = 0;
       ExplodePhase[x][y] = 1;
@@ -1660,6 +1684,11 @@ void Explode(int ex, int ey, int phase, int mode)
 
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
+
+    if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
+      element = Feld[x][y] = Back[x][y];
+    Back[x][y] = 0;
+
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
     InitField(x, y, FALSE);
     if (CAN_MOVE(element) || COULD_MOVE(element))
@@ -1678,14 +1707,19 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, phase - delay);
 
     if (phase == delay)
-      DrawCrumbledSand(SCREENX(x), SCREENY(y));
+      DrawLevelFieldCrumbledSand(x, y);
 
-    if (IS_PFORTE(Store[x][y]))
+    if (IS_WALKABLE_OVER(Back[x][y]))
     {
-      DrawLevelElement(x, y, Store[x][y]);
+      DrawLevelElement(x, y, Back[x][y]);
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
     }
-    else
+    else if (IS_WALKABLE_UNDER(Back[x][y]))
+    {
+      DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+      DrawLevelElementThruMask(x, y, Back[x][y]);
+    }
+    else if (!IS_WALKABLE_INSIDE(Back[x][y]))
       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
   }
 }
@@ -2423,11 +2457,11 @@ void TurnRound(int x, int y)
 
     if (IN_LEV_FIELD(left_x, left_y) &&
        (IS_FREE_OR_PLAYER(left_x, left_y) ||
-        IS_MAMPF2(Feld[left_x][left_y])))
+        IS_FOOD_DARK_YAMYAM(Feld[left_x][left_y])))
       can_turn_left = TRUE;
     if (IN_LEV_FIELD(right_x, right_y) &&
        (IS_FREE_OR_PLAYER(right_x, right_y) ||
-        IS_MAMPF2(Feld[right_x][right_y])))
+        IS_FOOD_DARK_YAMYAM(Feld[right_x][right_y])))
       can_turn_right = TRUE;
 
     if (can_turn_left && can_turn_right)
@@ -2714,7 +2748,7 @@ void TurnRound(int x, int y)
             Feld[newx][newy] == EL_ACID ||
             (element == EL_PENGUIN &&
              (Feld[newx][newy] == EL_EXIT_OPEN ||
-              IS_MAMPF3(Feld[newx][newy])))))
+              IS_FOOD_PENGUIN(Feld[newx][newy])))))
          return;
 
        MovDir[x][y] =
@@ -2726,7 +2760,7 @@ void TurnRound(int x, int y)
             Feld[newx][newy] == EL_ACID ||
             (element == EL_PENGUIN &&
              (Feld[newx][newy] == EL_EXIT_OPEN ||
-              IS_MAMPF3(Feld[newx][newy])))))
+              IS_FOOD_PENGUIN(Feld[newx][newy])))))
          return;
 
        MovDir[x][y] = old_move_dir;
@@ -3016,8 +3050,8 @@ void StartMoving(int x, int y)
 
     if (!MovDelay[x][y])       /* start new movement phase */
     {
-      /* all objects that can change their move direction after each step */
-      /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall  */
+      /* all objects that can change their move direction after each step
+        (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
 
       if (element != EL_YAMYAM &&
          element != EL_DARK_YAMYAM &&
@@ -3066,7 +3100,7 @@ void StartMoving(int x, int y)
          int flame_graphic = graphic + (i - 1);
 
          if (!IN_LEV_FIELD(xx, yy) ||
-             IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
+             IS_HISTORIC_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
            break;
 
          if (MovDelay[x][y])
@@ -3133,7 +3167,7 @@ void StartMoving(int x, int y)
        Feld[x][y] = EL_EMPTY;
        DrawLevelField(x, y);
 
-       PlaySoundLevel(newx, newy, SND_CLASS_EXIT_PASSING);
+       PlaySoundLevel(newx, newy, SND_PENGUIN_PASSING);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
          DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
 
@@ -3144,7 +3178,7 @@ void StartMoving(int x, int y)
 
        return;
       }
-      else if (IS_MAMPF3(Feld[newx][newy]))
+      else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
       {
        if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
          DrawLevelField(newx, newy);
@@ -3239,7 +3273,7 @@ void StartMoving(int x, int y)
       PlaySoundLevel(x, y, SND_YAMYAM_DIGGING);
     }
     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
-            IS_MAMPF2(Feld[newx][newy]))
+            IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
     {
       if (AmoebaNr[newx][newy])
       {
@@ -3387,7 +3421,7 @@ void ContinueMoving(int x, int y)
        yy = y + xy[i][1];
 
        if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_SAND)
-         DrawLevelField(xx, yy);       /* for "DrawCrumbledSand()" */
+         DrawLevelField(xx, yy);       /* for "crumbled sand" */
       }
     }
 
@@ -4131,22 +4165,22 @@ void MauerWaechst(int x, int y)
     {
       if (MovDir[x][y] == MV_LEFT)
       {
-       if (IN_LEV_FIELD(x - 1, y) && IS_MAUER(Feld[x - 1][y]))
+       if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
          DrawLevelField(x - 1, y);
       }
       else if (MovDir[x][y] == MV_RIGHT)
       {
-       if (IN_LEV_FIELD(x + 1, y) && IS_MAUER(Feld[x + 1][y]))
+       if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
          DrawLevelField(x + 1, y);
       }
       else if (MovDir[x][y] == MV_UP)
       {
-       if (IN_LEV_FIELD(x, y - 1) && IS_MAUER(Feld[x][y - 1]))
+       if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
          DrawLevelField(x, y - 1);
       }
       else
       {
-       if (IN_LEV_FIELD(x, y + 1) && IS_MAUER(Feld[x][y + 1]))
+       if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
          DrawLevelField(x, y + 1);
       }
 
@@ -4245,13 +4279,13 @@ void MauerAbleger(int ax, int ay)
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
     DrawLevelField(ax, ay);
 
-  if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
+  if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
     oben_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
+  if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
     unten_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
+  if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
     links_massiv = TRUE;
-  if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
+  if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
     rechts_massiv = TRUE;
 
   if (((oben_massiv && unten_massiv) ||
@@ -4372,7 +4406,7 @@ static void ChangeActiveTrap(int x, int y)
 
   /* if new animation frame was drawn, correct crumbled sand border */
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    DrawCrumbledSand(SCREENX(x), SCREENY(y));
+    DrawLevelFieldCrumbledSand(x, y);
 }
 
 static void ChangeElement(int x, int y)
@@ -4465,7 +4499,13 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
     CheckGravityMovement(player);
 
     if (player->MovPos == 0)
-      InitPlayerGfxAnimation(player, ACTION_DEFAULT);
+    {
+#if 0
+      printf("Trying... Player frame reset\n");
+#endif
+
+      InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
+    }
 
     if (player->MovPos == 0)   /* needed for tape.playing */
       player->is_moving = FALSE;
@@ -4865,7 +4905,7 @@ void GameActions()
       TimeLeft--;
 
       if (TimeLeft <= 10 && setup.time_limit)
-       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MAX_RIGHT);
+       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
 
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
 
@@ -4898,12 +4938,31 @@ void GameActions()
     redraw_mask |= REDRAW_FPS;
   }
 
+#if 0
+  if (stored_player[0].jx != stored_player[0].last_jx ||
+      stored_player[0].jy != stored_player[0].last_jy)
+    printf("::: %d, %d, %d, %d, %d\n",
+          stored_player[0].MovDir,
+          stored_player[0].MovPos,
+          stored_player[0].GfxPos,
+          stored_player[0].Frame,
+          stored_player[0].StepFrame);
+#endif
+
 #if 1
   FrameCounter++;
   TimeFrames++;
 
   for (i=0; i<MAX_PLAYERS; i++)
-    stored_player[i].Frame++;
+  {
+    int move_frames =
+      MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
+
+    stored_player[i].Frame += move_frames;
+
+    if (stored_player[i].MovPos != 0)
+      stored_player[i].StepFrame += move_frames;
+  }
 #endif
 }
 
@@ -4999,11 +5058,18 @@ static void CheckGravityMovement(struct PlayerInfo *player)
 
     if (field_under_player_is_free &&
        !player_is_moving_to_valid_field &&
-       !IS_TUBE(Feld[jx][jy]))
+       !IS_WALKABLE_INSIDE(Feld[jx][jy]))
       player->programmed_action = MV_DOWN;
   }
 }
 
+/*
+  MoveFigureOneStep()
+  -----------------------------------------------------------------------------
+  dx, dy:              direction (non-diagonal) to try to move the player to
+  real_dx, real_dy:    direction as read from input device (can be diagonal)
+*/
+
 boolean MoveFigureOneStep(struct PlayerInfo *player,
                          int dx, int dy, int real_dx, int real_dy)
 {
@@ -5200,7 +5266,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     }
   }
 
-#if 1
+#if 0
 #if 1
   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
 #else
@@ -5209,6 +5275,8 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
 #endif
 #endif
 
+  player->StepFrame = 0;
+
   if (moved & MF_MOVING)
   {
     if (old_jx != jx && old_jy == jy)
@@ -5216,7 +5284,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     else if (old_jx == jx && old_jy != jy)
       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
 
-    DrawLevelField(jx, jy);    /* for "DrawCrumbledSand()" */
+    DrawLevelField(jx, jy);    /* for "crumbled sand" */
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
@@ -5276,7 +5344,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
 
   if (player->MovPos == 0)
   {
-    if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
+    if (IS_PASSABLE(Feld[last_jx][last_jy]))
     {
       /* continue with normal speed after quickly moving through gate */
       HALVE_PLAYER_SPEED(player);
@@ -5564,8 +5632,8 @@ void KillHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
-  if (IS_PFORTE(Feld[jx][jy]))
-    Feld[jx][jy] = EL_EMPTY;
+  /* remove accessible field at the player's position */
+  Feld[jx][jy] = EL_EMPTY;
 
   /* deactivate shield (else Bang()/Explode() would not work right) */
   player->shield_normal_time_left = 0;
@@ -5617,6 +5685,59 @@ void RemoveHero(struct PlayerInfo *player)
   ExitY = ZY = jy;
 }
 
+/*
+  checkDiagonalPushing()
+  -----------------------------------------------------------------------------
+  check if diagonal input device direction results in pushing of object
+  (by checking if the alternative direction is walkable, diggable, ...)
+*/
+
+static boolean checkDiagonalPushing(struct PlayerInfo *player,
+                                   int x, int y, int real_dx, int real_dy)
+{
+  int jx = player->jx, jy = player->jy;
+  int dx = x - jx, dy = y - jy;
+
+  if (real_dx && real_dy)      /* diagonal direction input => do check */
+  {
+    /* diagonal direction: check alternative direction */
+    int xx = jx + (dx == 0 ? real_dx : 0);
+    int yy = jy + (dy == 0 ? real_dy : 0);
+
+    if (IN_LEV_FIELD(xx, yy))
+    {
+      int element = Feld[xx][yy];
+
+#if 0
+      if (IS_HISTORIC_SOLID(element) !=
+         (!(IS_WALKABLE(element) ||
+            IS_DIGGABLE(element) ||
+            IS_COLLECTIBLE(element))))
+       printf("::: %d ['%s'] [%d, %d]\n",
+              element,
+              element_info[element].token_name,
+              game.engine_version, tape.engine_version);
+#endif
+
+      if (game.engine_version < VERSION_IDENT(2,2,0))
+       return IS_HISTORIC_SOLID(element);
+      else
+       return !(IS_WALKABLE(element) ||
+                IS_DIGGABLE(element) ||
+                IS_COLLECTIBLE(element));
+    }
+  }
+
+  return TRUE;         /* no diagonal direction input => push object */
+}
+
+/*
+  DigField()
+  -----------------------------------------------------------------------------
+  x, y:                        field next to player (non-diagonal) to try to dig to
+  real_dx, real_dy:    direction as read from input device (can be diagonal)
+*/
+
 int DigField(struct PlayerInfo *player,
             int x, int y, int real_dx, int real_dy, int mode)
 {
@@ -5628,7 +5749,11 @@ int DigField(struct PlayerInfo *player,
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
-  player->is_digging = FALSE;
+  if (player->MovPos == 0)
+  {
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
+  }
 
   if (player->MovPos == 0)
     player->Pushing = FALSE;
@@ -5643,9 +5768,15 @@ int DigField(struct PlayerInfo *player,
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
     return MF_NO_ACTION;
 
-  if (IS_TUBE(Feld[jx][jy]))
+#if 0
+  if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
+#else
+  if (IS_TUBE(Feld[jx][jy]) ||
+      (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
+#endif
   {
     int i = 0;
+    int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
     int tube_leave_directions[][2] =
     {
       { EL_TUBE_ANY,                   MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
@@ -5662,7 +5793,7 @@ int DigField(struct PlayerInfo *player,
       { -1,                            MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
     };
 
-    while (tube_leave_directions[i][0] != Feld[jx][jy])
+    while (tube_leave_directions[i][0] != tube_element)
     {
       i++;
       if (tube_leave_directions[i][0] == -1)   /* should not happen */
@@ -5685,11 +5816,14 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_BASE:
     case EL_SP_BUGGY_BASE:
     case EL_SP_BUGGY_BASE_ACTIVATING:
+      RemoveField(x, y);
 #if 1
-      if (mode != DF_SNAP && element == EL_SAND)
-       GfxElement[x][y] = Feld[x][y];
+      if (mode != DF_SNAP && element != EL_EMPTY)
+      {
+       GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element);
+       player->is_digging = TRUE;
+      }
 #endif
-      RemoveField(x, y);
       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
       break;
 
@@ -5703,6 +5837,13 @@ int DigField(struct PlayerInfo *player,
     case EL_PEARL:
     case EL_CRYSTAL:
       RemoveField(x, y);
+#if 1
+      if (mode != DF_SNAP)
+      {
+       GfxElement[x][y] = element;
+       player->is_collecting = TRUE;
+      }
+#endif
       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
                                          element == EL_PEARL ? 5 :
                                          element == EL_CRYSTAL ? 8 : 1);
@@ -5732,7 +5873,7 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
       }
-      PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MAX_RIGHT);
+      PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MIDDLE);
       break;
 
     case EL_SHIELD_NORMAL:
@@ -5943,11 +6084,17 @@ int DigField(struct PlayerInfo *player,
       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
        return MF_NO_ACTION;
 
+#if 1
+      if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+       return MF_NO_ACTION;
+#else
       if (real_dy)
       {
-       if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
+       if (IN_LEV_FIELD(jx, jy+real_dy) &&
+           !IS_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
          return MF_NO_ACTION;
       }
+#endif
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6051,16 +6198,16 @@ int DigField(struct PlayerInfo *player,
       break;
 
     case EL_SP_PORT_LEFT:
-    case EL_SP_GRAVITY_PORT_LEFT:
     case EL_SP_PORT_RIGHT:
-    case EL_SP_GRAVITY_PORT_RIGHT:
     case EL_SP_PORT_UP:
-    case EL_SP_GRAVITY_PORT_UP:
     case EL_SP_PORT_DOWN:
-    case EL_SP_GRAVITY_PORT_DOWN:
     case EL_SP_PORT_HORIZONTAL:
     case EL_SP_PORT_VERTICAL:
     case EL_SP_PORT_ANY:
+    case EL_SP_GRAVITY_PORT_LEFT:
+    case EL_SP_GRAVITY_PORT_RIGHT:
+    case EL_SP_GRAVITY_PORT_UP:
+    case EL_SP_GRAVITY_PORT_DOWN:
       if ((dx == -1 &&
           element != EL_SP_PORT_LEFT &&
           element != EL_SP_GRAVITY_PORT_LEFT &&
@@ -6166,7 +6313,7 @@ int DigField(struct PlayerInfo *player,
       TimeLeft += 10;
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
       DrawLevelField(x, y);
-      PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MAX_RIGHT);
+      PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
       return MF_ACTION;
       break;
 
@@ -6189,16 +6336,23 @@ int DigField(struct PlayerInfo *player,
                  || !IS_SB_ELEMENT(element))))
        return MF_NO_ACTION;
 
+#if 1
+      if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+       return MF_NO_ACTION;
+#else
       if (dx && real_dy)
       {
-       if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
+       if (IN_LEV_FIELD(jx, jy+real_dy) &&
+           !IS_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
          return MF_NO_ACTION;
       }
       else if (dy && real_dx)
       {
-       if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
+       if (IN_LEV_FIELD(jx+real_dx, jy) &&
+           !IS_HISTORIC_SOLID(Feld[jx+real_dx][jy]))
          return MF_NO_ACTION;
       }
+#endif
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6298,16 +6452,23 @@ int DigField(struct PlayerInfo *player,
        if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
          return MF_NO_ACTION;
 
+#if 1
+       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
+         return MF_NO_ACTION;
+#else
        if (dx && real_dy)
        {
-         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
+         if (IN_LEV_FIELD(jx, jy+real_dy) &&
+             !IS_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
            return MF_NO_ACTION;
        }
        else if (dy && real_dx)
        {
-         if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
+         if (IN_LEV_FIELD(jx+real_dx, jy) &&
+             !IS_HISTORIC_SOLID(Feld[jx+real_dx][jy]))
            return MF_NO_ACTION;
        }
+#endif
 
        if (player->push_delay == 0)
          player->push_delay = FrameCounter;
@@ -6332,8 +6493,8 @@ int DigField(struct PlayerInfo *player,
 
   player->push_delay = 0;
 
-  if (Feld[x][y] != element)           /* really digged something */
-    player->is_digging = TRUE;
+  if (Feld[x][y] != element)           /* really digged/collected something */
+    player->is_collecting = !player->is_digging;
 
   return MF_MOVING;
 }
@@ -6355,7 +6516,13 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
       player->Pushing = FALSE;
 
     player->snapped = FALSE;
-    player->is_digging = FALSE;
+
+    if (player->MovPos == 0)
+    {
+      player->is_digging = FALSE;
+      player->is_collecting = FALSE;
+    }
+
     return FALSE;
   }
 
@@ -6372,6 +6539,8 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   player->snapped = TRUE;
   player->is_digging = FALSE;
+  player->is_collecting = FALSE;
+
   DrawLevelField(x, y);
   BackToFront();
 
@@ -6392,8 +6561,19 @@ boolean PlaceBomb(struct PlayerInfo *player)
       IS_ACTIVE_BOMB(element) || element == EL_EXPLOSION)
     return FALSE;
 
+#if 0
   if (element != EL_EMPTY)
+    return FALSE;
+#endif
+
+  if (element != EL_EMPTY)
+  {
+#if 0
     Store[jx][jy] = element;
+#else
+    Back[jx][jy] = element;
+#endif
+  }
 
   MovDelay[jx][jy] = 96;
 
@@ -6556,22 +6736,22 @@ void RaiseScoreElement(int element)
     case EL_EMERALD_YELLOW:
     case EL_EMERALD_RED:
     case EL_EMERALD_PURPLE:
-      RaiseScore(level.score[SC_EDELSTEIN]);
+      RaiseScore(level.score[SC_EMERALD]);
       break;
     case EL_DIAMOND:
-      RaiseScore(level.score[SC_DIAMANT]);
+      RaiseScore(level.score[SC_DIAMOND]);
       break;
     case EL_BUG:
     case EL_BD_BUTTERFLY:
-      RaiseScore(level.score[SC_KAEFER]);
+      RaiseScore(level.score[SC_BUG]);
       break;
     case EL_SPACESHIP:
     case EL_BD_FIREFLY:
-      RaiseScore(level.score[SC_FLIEGER]);
+      RaiseScore(level.score[SC_SPACESHIP]);
       break;
     case EL_YAMYAM:
     case EL_DARK_YAMYAM:
-      RaiseScore(level.score[SC_MAMPFER]);
+      RaiseScore(level.score[SC_YAMYAM]);
       break;
     case EL_ROBOT:
       RaiseScore(level.score[SC_ROBOT]);
@@ -6580,16 +6760,16 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_PACMAN]);
       break;
     case EL_NUT:
-      RaiseScore(level.score[SC_KOKOSNUSS]);
+      RaiseScore(level.score[SC_NUT]);
       break;
     case EL_DYNAMITE:
-      RaiseScore(level.score[SC_DYNAMIT]);
+      RaiseScore(level.score[SC_DYNAMITE]);
       break;
     case EL_KEY_1:
     case EL_KEY_2:
     case EL_KEY_3:
     case EL_KEY_4:
-      RaiseScore(level.score[SC_SCHLUESSEL]);
+      RaiseScore(level.score[SC_KEY]);
       break;
     default:
       break;