rnd-20030521-1-src
[rocksndiamonds.git] / src / game.c
index 1736366565c50950c217301519f37d01f8a42ec1..38cf088dd0f87ef0305e781d012d7b6a6de46ae8 100644 (file)
@@ -14,9 +14,9 @@
 #include "libgame/libgame.h"
 
 #include "game.h"
+#include "init.h"
 #include "tools.h"
 #include "screens.h"
-#include "init.h"
 #include "files.h"
 #include "tape.h"
 #include "network.h"
@@ -154,38 +154,144 @@ struct ChangingElementInfo
 
 static struct ChangingElementInfo changing_element_list[] =
 {
-  { EL_NUT_BREAKING,           EL_EMERALD,              6, NULL, NULL, NULL },
-  { EL_PEARL_BREAKING,         EL_EMPTY,                8, NULL, NULL, NULL },
-  { EL_EXIT_OPENING,           EL_EXIT_OPEN,           29, NULL, NULL, NULL },
-
-  { EL_SWITCHGATE_OPENING,     EL_SWITCHGATE_OPEN,     29, NULL, NULL, NULL },
-  { EL_SWITCHGATE_CLOSING,     EL_SWITCHGATE_CLOSED,   29, NULL, NULL, NULL },
-
-  { EL_TIMEGATE_OPENING,       EL_TIMEGATE_OPEN,       29, NULL, NULL, NULL },
-  { EL_TIMEGATE_CLOSING,       EL_TIMEGATE_CLOSED,     29, NULL, NULL, NULL },
-
-  { EL_ACID_SPLASH_LEFT,       EL_EMPTY,                8, NULL, NULL, NULL },
-  { EL_ACID_SPLASH_RIGHT,      EL_EMPTY,                8, NULL, NULL, NULL },
-
-  { EL_SP_BUGGY_BASE,          EL_SP_BUGGY_BASE_ACTIVATING, 0,
-    InitBuggyBase, NULL, NULL },
-  { EL_SP_BUGGY_BASE_ACTIVATING,EL_SP_BUGGY_BASE_ACTIVE, 0,
-    InitBuggyBase, NULL, NULL },
-  { EL_SP_BUGGY_BASE_ACTIVE,   EL_SP_BUGGY_BASE,        0,
-    InitBuggyBase, WarnBuggyBase, NULL },
-
-  { EL_TRAP,                   EL_TRAP_ACTIVE,          0,
-    InitTrap, NULL, ActivateTrap },
-  { EL_TRAP_ACTIVE,            EL_TRAP,                31,
-    NULL, ChangeActiveTrap, NULL },
-
-  { EL_ROBOT_WHEEL_ACTIVE,     EL_ROBOT_WHEEL,          0,
-    InitRobotWheel, RunRobotWheel, StopRobotWheel },
+  {
+    EL_NUT_BREAKING,
+    EL_EMERALD,
+    6,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_PEARL_BREAKING,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EXIT_OPENING,
+    EL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_OPENING,
+    EL_SWITCHGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_CLOSING,
+    EL_SWITCHGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_OPENING,
+    EL_TIMEGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_CLOSING,
+    EL_TIMEGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
 
-  { EL_TIMEGATE_SWITCH_ACTIVE, EL_TIMEGATE_SWITCH,      0,
-    InitTimegateWheel, RunTimegateWheel, NULL },
+  {
+    EL_ACID_SPLASH_LEFT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_ACID_SPLASH_RIGHT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE,
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    EL_SP_BUGGY_BASE_ACTIVE,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVE,
+    EL_SP_BUGGY_BASE,
+    0,
+    InitBuggyBase,
+    WarnBuggyBase,
+    NULL
+  },
+  {
+    EL_TRAP,
+    EL_TRAP_ACTIVE,
+    0,
+    InitTrap,
+    NULL,
+    ActivateTrap
+  },
+  {
+    EL_TRAP_ACTIVE,
+    EL_TRAP,
+    31,
+    NULL,
+    ChangeActiveTrap,
+    NULL
+  },
+  {
+    EL_ROBOT_WHEEL_ACTIVE,
+    EL_ROBOT_WHEEL,
+    0,
+    InitRobotWheel,
+    RunRobotWheel,
+    StopRobotWheel
+  },
+  {
+    EL_TIMEGATE_SWITCH_ACTIVE,
+    EL_TIMEGATE_SWITCH,
+    0,
+    InitTimegateWheel,
+    RunTimegateWheel,
+    NULL
+  },
 
-  { EL_UNDEFINED,              EL_UNDEFINED,           -1, NULL        }
+  {
+    EL_UNDEFINED,
+    EL_UNDEFINED,
+    -1,
+    NULL,
+    NULL,
+    NULL
+  }
 };
 
 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
@@ -500,9 +606,13 @@ static void InitGameEngine()
 {
   int i;
 
+  /* set game engine from tape file when re-playing, else from level file */
   game.engine_version = (tape.playing ? tape.engine_version :
                         level.game_version);
 
+  /* dynamically adjust element properties according to game engine version */
+  InitElementPropertiesEngine(game.engine_version);
+
 #if 0
     printf("level %d: level version == %06d\n", level_nr, level.game_version);
     printf("          tape version == %06d [%s] [file: %06d]\n",
@@ -520,6 +630,7 @@ static void InitGameEngine()
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
+#if 0
   /* dynamically adjust element properties according to game engine version */
   {
     static int ep_em_slippery_wall[] =
@@ -543,6 +654,7 @@ static void InitGameEngine()
                 (level.em_slippery_gems &&
                  game.engine_version > VERSION_IDENT(2,0,1)));
   }
+#endif
 
   /* initialize changing elements information */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
@@ -555,6 +667,7 @@ static void InitGameEngine()
     changing_element[i].post_change_function = NULL;
   }
 
+  /* add changing elements from pre-defined list */
   i = 0;
   while (changing_element_list[i].base_element != EL_UNDEFINED)
   {
@@ -570,6 +683,30 @@ static void InitGameEngine()
 
     i++;
   }
+
+  /* add changing elements from custom element configuration */
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    struct CustomElementChangeInfo *change = &level.custom_element[i].change;
+    int element = EL_CUSTOM_START + i;
+
+    /* only add custom elements that change after fixed/random frame delay */
+    if (!IS_CHANGEABLE(element) ||
+       (!HAS_CHANGE_EVENT(element, CE_DELAY_FIXED) &&
+        !HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM)))
+      continue;
+
+    changing_element[element].base_element = element;
+    changing_element[element].next_element = change->successor;
+    changing_element[element].change_delay = 0;
+
+    if (HAS_CHANGE_EVENT(element, CE_DELAY_FIXED))
+      changing_element[element].change_delay +=
+       change->delay_fixed * change->delay_frames;
+
+    if (HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM));
+    /* random frame delay added at runtime for each element individually */
+  }
 }
 
 
@@ -699,7 +836,7 @@ void InitGame()
 
   AllPlayersGone = FALSE;
 
-  game.yam_content_nr = 0;
+  game.yamyam_content_nr = 0;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
   game.light_time_left = 0;
@@ -941,8 +1078,7 @@ void InitGame()
   if (setup.sound_music)
     PlayMusic(level_nr);
 
-  if (!tape.playing)
-    KeyboardAutoRepeatOff();
+  KeyboardAutoRepeatOffUnlessAutoplay();
 
   if (options.debug)
   {
@@ -1177,7 +1313,7 @@ void GameWon()
 
   if ((hi_pos = NewHiScore()) >= 0) 
   {
-    game_status = HALLOFFAME;
+    game_status = GAME_MODE_SCORES;
     DrawHallOfFame(hi_pos);
     if (raise_level)
     {
@@ -1187,7 +1323,7 @@ void GameWon()
   }
   else
   {
-    game_status = MAINMENU;
+    game_status = GAME_MODE_MAIN;
     if (raise_level)
     {
       level_nr++;
@@ -1518,7 +1654,7 @@ void Explode(int ex, int ey, int phase, int mode)
       Feld[ex][ey] = center_element;
     }
 
-    for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++)
+    for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
     {
       int element;
 
@@ -1535,11 +1671,16 @@ void Explode(int ex, int ey, int phase, int mode)
        RemoveMovingField(x, y);
       }
 
+#if 1
+      if (IS_EXPLOSION_PROOF(element))
+       continue;
+#else
       if ((IS_INDESTRUCTIBLE(element) &&
           (game.engine_version < VERSION_IDENT(2,2,0) ||
            (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
          element == EL_FLAMES)
        continue;
+#endif
 
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
       {
@@ -1593,10 +1734,14 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_BD_DIAMOND;
       else if (center_element == EL_SP_ELECTRON)
        Store[x][y] = EL_SP_INFOTRON;
-      else if (center_element == EL_YAMYAM)
-       Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
       else if (center_element == EL_AMOEBA_TO_DIAMOND)
        Store[x][y] = level.amoeba_content;
+      else if (center_element == EL_YAMYAM)
+       Store[x][y] =
+         level.yamyam_content[game.yamyam_content_nr][x - ex + 1][y - ey + 1];
+      else if (IS_CUSTOM_ELEMENT(center_element))
+       Store[x][y] =
+         CUSTOM_ELEMENT_INFO(center_element).content[x - ex + 1][y - ey + 1];
       else if (element == EL_WALL_EMERALD)
        Store[x][y] = EL_EMERALD;
       else if (element == EL_WALL_DIAMOND)
@@ -1638,7 +1783,8 @@ void Explode(int ex, int ey, int phase, int mode)
     }
 
     if (center_element == EL_YAMYAM)
-      game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
+      game.yamyam_content_nr =
+       (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
 
     return;
   }
@@ -1668,7 +1814,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_PLAYER(x, y))
       KillHeroUnlessProtected(x, y);
-    else if (IS_EXPLOSIVE(element))
+    else if (CAN_EXPLODE(element))
     {
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
@@ -1691,7 +1837,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
     InitField(x, y, FALSE);
-    if (CAN_MOVE(element) || COULD_MOVE(element))
+    if (CAN_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
 
@@ -1709,7 +1855,7 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       DrawLevelFieldCrumbledSand(x, y);
 
-    if (IS_WALKABLE_OVER(Back[x][y]))
+    if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
       DrawLevelElement(x, y, Back[x][y]);
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
@@ -2219,7 +2365,7 @@ void Impact(int x, int y)
 
   if (!lastline && object_hit)         /* check which object was hit */
   {
-    if (CAN_CHANGE(element) && 
+    if (CAN_PASS_MAGIC_WALL(element) && 
        (smashed == EL_MAGIC_WALL ||
         smashed == EL_BD_MAGIC_WALL))
     {
@@ -2508,13 +2654,13 @@ void TurnRound(int x, int y)
     int rnd = RND(rnd_value);
 
     if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
+       (IS_FREE(left_x, left_y) || IS_FOOD_PIG(Feld[left_x][left_y])))
       can_turn_left = TRUE;
     if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
+       (IS_FREE(right_x, right_y) || IS_FOOD_PIG(Feld[right_x][right_y])))
       can_turn_right = TRUE;
     if (IN_LEV_FIELD(move_x, move_y) &&
-       (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
+       (IS_FREE(move_x, move_y) || IS_FOOD_PIG(Feld[move_x][move_y])))
       can_move_on = TRUE;
 
     if (can_turn_left &&
@@ -2566,7 +2712,7 @@ void TurnRound(int x, int y)
       MovDir[x][y] = back_dir;
 
     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
-       !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
+       !IS_FOOD_PIG(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
       MovDir[x][y] = old_move_dir;
 
     MovDelay[x][y] = 0;
@@ -2793,7 +2939,7 @@ static boolean JustBeingPushed(int x, int y)
 
 void StartMoving(int x, int y)
 {
-  static boolean use_spring_bug = TRUE;
+  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
   boolean started_moving = FALSE;      /* some elements can fall _and_ move */
   int element = Feld[x][y];
 
@@ -2916,7 +3062,7 @@ void StartMoving(int x, int y)
        Store[x][y] = 0;
       }
     }
-    else if (CAN_CHANGE(element) &&
+    else if (CAN_PASS_MAGIC_WALL(element) &&
             (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
              Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
     {
@@ -3099,15 +3245,14 @@ void StartMoving(int x, int y)
          int sx = SCREENX(xx), sy = SCREENY(yy);
          int flame_graphic = graphic + (i - 1);
 
-         if (!IN_LEV_FIELD(xx, yy) ||
-             IS_HISTORIC_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
+         if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
            break;
 
          if (MovDelay[x][y])
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
-           if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
+           if (IS_ENEMY(flamed) || CAN_EXPLODE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
@@ -3196,7 +3341,7 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
     {
-      if (IS_GEM(Feld[newx][newy]))
+      if (IS_FOOD_PIG(Feld[newx][newy]))
       {
        if (IS_MOVING(newx, newy))
          RemoveMovingField(newx, newy);
@@ -4413,10 +4558,23 @@ static void ChangeElement(int x, int y)
 {
   int element = Feld[x][y];
 
+  if (IS_MOVING(x, y))                 /* never change a running system :-) */
+    return;
+
   if (MovDelay[x][y] == 0)             /* initialize element change */
   {
     MovDelay[x][y] = changing_element[element].change_delay + 1;
 
+    if (IS_CUSTOM_ELEMENT(element) &&
+       HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM))
+    {
+      int i = element - EL_CUSTOM_START;
+      int max_random_delay = level.custom_element[i].change.delay_random;
+      int delay_frames = level.custom_element[i].change.delay_frames;
+
+      MovDelay[x][y] += RND(max_random_delay * delay_frames);
+    }
+
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
@@ -4441,6 +4599,11 @@ static void ChangeElement(int x, int y)
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
+#if 1
+    InitField(x, y, FALSE);
+    if (CAN_MOVE(element))
+      InitMovDir(x, y);
+#endif
     DrawLevelField(x, y);
 
     if (changing_element[element].post_change_function)
@@ -4527,7 +4690,7 @@ void GameActions()
   byte *recorded_player_action;
   byte summarized_player_action = 0;
 
-  if (game_status != PLAYING)
+  if (game_status != GAME_MODE_PLAYING)
     return;
 
   action_delay_value =
@@ -4553,7 +4716,7 @@ void GameActions()
     HandleNetworking();
 #endif
 
-    if (game_status != PLAYING)
+    if (game_status != GAME_MODE_PLAYING)
       return;
 
     if (!network_player_action_received)
@@ -4644,6 +4807,15 @@ void GameActions()
     element = Feld[x][y];
     graphic = el2img(element);
 
+#if 0
+    if (element == -1)
+    {
+      printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
+
+      element = graphic = 0;
+    }
+#endif
+
     if (graphic_info[graphic].anim_global_sync)
       GfxFrame[x][y] = FrameCounter;
 
@@ -4720,13 +4892,21 @@ void GameActions()
       MauerAbleger(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
+#if 0
     else if (IS_AUTO_CHANGING(element))
       ChangeElement(x, y);
+#endif
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (IS_ANIMATED(graphic))
+    else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#if 1
+    /* this may take place after moving, therefore element may have changed */
+    if (IS_AUTO_CHANGING(Feld[x][y]))
+      ChangeElement(x, y);
+#endif
+
     if (IS_BELT_ACTIVE(element))
       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
 
@@ -5695,40 +5875,20 @@ void RemoveHero(struct PlayerInfo *player)
 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);
+  int jx, jy, dx, dy, xx, yy;
 
-    if (IN_LEV_FIELD(xx, yy))
-    {
-      int element = Feld[xx][yy];
+  if (real_dx == 0 || real_dy == 0)    /* no diagonal direction => push */
+    return TRUE;
 
-#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));
-    }
-  }
+  /* diagonal direction: check alternative direction */
+  jx = player->jx;
+  jy = player->jy;
+  dx = x - jx;
+  dy = y - jy;
+  xx = jx + (dx == 0 ? real_dx : 0);
+  yy = jy + (dy == 0 ? real_dy : 0);
 
-  return TRUE;         /* no diagonal direction input => push object */
+  return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
 }
 
 /*
@@ -5762,6 +5922,7 @@ int DigField(struct PlayerInfo *player,
   {
     player->Switching = FALSE;
     player->push_delay = 0;
+
     return MF_NO_ACTION;
   }
 
@@ -5806,6 +5967,12 @@ int DigField(struct PlayerInfo *player,
 
   element = Feld[x][y];
 
+#if 1
+  if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
+      game.engine_version >= VERSION_IDENT(2,2,0))
+    return MF_NO_ACTION;
+#endif
+
   switch (element)
   {
     case EL_EMPTY:
@@ -6081,20 +6248,17 @@ int DigField(struct PlayerInfo *player,
 
       player->Pushing = TRUE;
 
+#if 0
+      if (element == EL_ROCK)
+       printf("::: wanna push [%d] [%d]\n",
+              FrameCounter, player->push_delay_value);
+#endif
+
       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_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
-         return MF_NO_ACTION;
-      }
-#endif
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6336,23 +6500,8 @@ 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_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_HISTORIC_SOLID(Feld[jx+real_dx][jy]))
-         return MF_NO_ACTION;
-      }
-#endif
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6439,7 +6588,41 @@ int DigField(struct PlayerInfo *player,
       break;
 
     default:
-      if (IS_PUSHABLE(element))
+
+      if (IS_WALKABLE(element))
+      {
+       break;
+      }
+      else if (IS_DIGGABLE(element))
+      {
+       RemoveField(x, y);
+#if 1
+       if (mode != DF_SNAP)
+       {
+         GfxElement[x][y] =
+           (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
+         player->is_digging = TRUE;
+       }
+#endif
+       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
+
+       break;
+      }
+      else if (IS_COLLECTIBLE(element))
+      {
+       RemoveField(x, y);
+#if 1
+       if (mode != DF_SNAP)
+       {
+         GfxElement[x][y] = element;
+         player->is_collecting = TRUE;
+       }
+#endif
+       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+
+       break;
+      }
+      else if (IS_PUSHABLE(element))
       {
        if (mode == DF_SNAP)
          return MF_NO_ACTION;
@@ -6452,23 +6635,8 @@ 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_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_HISTORIC_SOLID(Feld[jx+real_dx][jy]))
-           return MF_NO_ACTION;
-       }
-#endif
 
        if (player->push_delay == 0)
          player->push_delay = FrameCounter;
@@ -6504,6 +6672,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
 
+  if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
+    return FALSE;
+
   if (!player->active || !IN_LEV_FIELD(x, y))
     return FALSE;
 
@@ -6534,7 +6705,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                    dy < 0 ? MV_UP :
                    dy > 0 ? MV_DOWN :  MV_NO_MOVING);
 
-  if (!DigField(player, x, y, 0, 0, DF_SNAP))
+  if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
 
   player->snapped = TRUE;
@@ -6736,17 +6907,26 @@ void RaiseScoreElement(int element)
     case EL_EMERALD_YELLOW:
     case EL_EMERALD_RED:
     case EL_EMERALD_PURPLE:
+    case EL_SP_INFOTRON:
       RaiseScore(level.score[SC_EMERALD]);
       break;
     case EL_DIAMOND:
       RaiseScore(level.score[SC_DIAMOND]);
       break;
+    case EL_CRYSTAL:
+      RaiseScore(level.score[SC_CRYSTAL]);
+      break;
+    case EL_PEARL:
+      RaiseScore(level.score[SC_PEARL]);
+      break;
     case EL_BUG:
     case EL_BD_BUTTERFLY:
+    case EL_SP_ELECTRON:
       RaiseScore(level.score[SC_BUG]);
       break;
     case EL_SPACESHIP:
     case EL_BD_FIREFLY:
+    case EL_SP_SNIKSNAK:
       RaiseScore(level.score[SC_SPACESHIP]);
       break;
     case EL_YAMYAM:
@@ -6763,8 +6943,18 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_NUT]);
       break;
     case EL_DYNAMITE:
+    case EL_DYNABOMB_INCREASE_NUMBER:
+    case EL_DYNABOMB_INCREASE_SIZE:
+    case EL_DYNABOMB_INCREASE_POWER:
       RaiseScore(level.score[SC_DYNAMITE]);
       break;
+    case EL_SHIELD_NORMAL:
+    case EL_SHIELD_DEADLY:
+      RaiseScore(level.score[SC_SHIELD]);
+      break;
+    case EL_EXTRA_TIME:
+      RaiseScore(level.score[SC_TIME_BONUS]);
+      break;
     case EL_KEY_1:
     case EL_KEY_2:
     case EL_KEY_3:
@@ -6790,7 +6980,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
     else
 #endif
     {
-      game_status = MAINMENU;
+      game_status = GAME_MODE_MAIN;
       DrawMainMenu();
     }
   }
@@ -6951,7 +7141,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 {
   int id = gi->custom_id;
 
-  if (game_status != PLAYING)
+  if (game_status != GAME_MODE_PLAYING)
     return;
 
   switch (id)