rnd-20060128-1-src
[rocksndiamonds.git] / src / game.c
index 052239346ffc65f545296b98070c120881393cf3..2ac9f0d03941d55f96b0091efe3c70dfe8aea41e 100644 (file)
 #define USE_NEW_DELAYED_ACTION         (USE_NEW_STUFF          * 1)
 #define USE_NEW_SNAP_DELAY             (USE_NEW_STUFF          * 1)
 #define USE_ONLY_ONE_CHANGE_PER_FRAME  (USE_NEW_STUFF          * 1)
+#define USE_ONE_MORE_CHANGE_PER_FRAME  (USE_NEW_STUFF          * 1)
+#define USE_FIXED_DONT_RUN_INTO                (USE_NEW_STUFF          * 1)
+#define USE_NEW_SPRING_BUMPER          (USE_NEW_STUFF          * 1)
+
 #define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
 
 /* for DigField() */
 #define DF_SNAP                        2
 
 /* for MovePlayer() */
-#define MF_NO_ACTION           0
-#define MF_MOVING              1
-#define MF_ACTION              2
+#define MP_NO_ACTION           0
+#define MP_MOVING              1
+#define MP_ACTION              2
+#define MP_DONT_RUN_INTO       (MP_MOVING | MP_ACTION)
 
 /* for ScrollPlayer() */
 #define SCROLL_INIT            0
 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                        \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
 
+#define ANDROID_CAN_ENTER_FIELD(e, x, y)                               \
+       ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
+
 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                 \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
 
 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                        \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
 
+#define SPRING_CAN_BUMP_FROM_FIELD(x, y)                               \
+       (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
+                               Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
+
 #define GROUP_NR(e)            ((e) - EL_GROUP_START)
 #define MOVE_ENTER_EL(e)       (element_info[e].move_enter_element)
 #define IS_IN_GROUP(e, nr)     (element_info[e].in_group[nr] == TRUE)
@@ -321,6 +333,8 @@ static void RunTimegateWheel(int, int);
 static void InitMagicBallDelay(int, int);
 static void ActivateMagicBall(int, int);
 
+static void InitDiagonalMovingElement(int, int);
+
 struct ChangingElementInfo
 {
   int element;
@@ -494,6 +508,30 @@ static struct ChangingElementInfo change_delay_list[] =
     NULL,
     ActivateMagicBall
   },
+  {
+    EL_EMC_SPRING_BUMPER_ACTIVE,
+    EL_EMC_SPRING_BUMPER,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_DIAGONAL_SHRINKING,
+    EL_EMPTY,
+    0,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_DIAGONAL_GROWING,
+    EL_EMPTY,
+    0,
+    NULL,
+    NULL,
+    InitDiagonalMovingElement
+  },
 
   {
     EL_UNDEFINED,
@@ -654,6 +692,40 @@ static void InitPlayfieldScanMode(int mode)
   InitPlayfieldScanModeVars();
 }
 
+static int get_move_delay_from_stepsize(int move_stepsize)
+{
+  move_stepsize =
+    MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
+
+  /* make sure that stepsize value is always a power of 2 */
+  move_stepsize = (1 << log_2(move_stepsize));
+
+  return TILEX / move_stepsize;
+}
+
+static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
+                              boolean init_game)
+{
+  int move_delay = get_move_delay_from_stepsize(move_stepsize);
+  boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
+
+  /* do no immediately change move delay -- the player might just be moving */
+  player->move_delay_value_next = move_delay;
+
+  /* information if player can move must be set separately */
+  player->cannot_move = cannot_move;
+
+  if (init_game)
+  {
+    player->move_delay       = game.initial_move_delay;
+    player->move_delay_value = game.initial_move_delay_value;
+
+    player->move_delay_value_next = -1;
+
+    player->move_delay_reset_counter = 0;
+  }
+}
+
 void GetPlayerConfig()
 {
   if (!audio.sound_available)
@@ -1341,9 +1413,15 @@ static void InitGameEngine()
 
   /* ---------- initialize player's initial move delay --------------------- */
 
+#if 1
+  /* dynamically adjust player properties according to level information */
+  game.initial_move_delay_value =
+    get_move_delay_from_stepsize(level.initial_player_stepsize);
+#else
   /* dynamically adjust player properties according to level information */
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
+#endif
 
   /* dynamically adjust player properties according to game engine version */
   game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
@@ -1724,8 +1802,6 @@ void InitGame()
     player->is_bored = FALSE;
     player->is_sleeping = FALSE;
 
-    player->cannot_move = FALSE;
-
     player->frame_counter_bored = -1;
     player->frame_counter_sleeping = -1;
 
@@ -1753,6 +1829,9 @@ void InitGame()
 
     player->show_envelope = 0;
 
+#if 1
+    SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
+#else
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
 
@@ -1760,6 +1839,9 @@ void InitGame()
 
     player->move_delay_reset_counter = 0;
 
+    player->cannot_move = FALSE;
+#endif
+
     player->push_delay       = -1;     /* initialized when pushing starts */
     player->push_delay_value = game.initial_push_delay_value;
 
@@ -2756,8 +2838,13 @@ void InitMovingField(int x, int y, int direction)
 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
 {
   int direction = MovDir[x][y];
+#if 1
+  int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
+  int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
+#else
   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+#endif
 
   *goes_to_x = newx;
   *goes_to_y = newy;
@@ -4606,16 +4693,35 @@ inline static void TurnRoundExt(int x, int y)
   }
   else if (element == EL_SPRING)
   {
+#if USE_NEW_SPRING_BUMPER
+    if (MovDir[x][y] & MV_HORIZONTAL)
+    {
+      if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
+         !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
+      {
+       Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
+       ResetGfxAnimation(move_x, move_y);
+       DrawLevelField(move_x, move_y);
+
+       MovDir[x][y] = back_dir;
+      }
+      else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
+              SPRING_CAN_ENTER_FIELD(element, x, y + 1))
+       MovDir[x][y] = MV_NONE;
+    }
+#else
     if (MovDir[x][y] & MV_HORIZONTAL &&
        (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
         SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
       MovDir[x][y] = MV_NONE;
+#endif
 
     MovDelay[x][y] = 0;
   }
   else if (element == EL_ROBOT ||
           element == EL_SATELLITE ||
-          element == EL_PENGUIN)
+          element == EL_PENGUIN ||
+          element == EL_EMC_ANDROID)
   {
     int attr_x = -1, attr_y = -1;
 
@@ -4716,21 +4822,21 @@ inline static void TurnRoundExt(int x, int y)
          new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
        Moving2Blocked(x, y, &newx, &newy);
 
-       if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
+       if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
          return;
 
        MovDir[x][y] =
          new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
        Moving2Blocked(x, y, &newx, &newy);
 
-       if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
+       if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
          return;
 
        MovDir[x][y] = old_move_dir;
        return;
       }
     }
-    else       /* (element == EL_SATELLITE) */
+    else if (element == EL_SATELLITE)
     {
       int newx, newy;
 
@@ -4759,6 +4865,71 @@ inline static void TurnRoundExt(int x, int y)
        return;
       }
     }
+    else if (element == EL_EMC_ANDROID)
+    {
+      static int check_pos[16] =
+      {
+       -1,             /*  0 => (invalid)          */
+       7,              /*  1 => MV_LEFT            */
+       3,              /*  2 => MV_RIGHT           */
+       -1,             /*  3 => (invalid)          */
+       1,              /*  4 =>            MV_UP   */
+       0,              /*  5 => MV_LEFT  | MV_UP   */
+       2,              /*  6 => MV_RIGHT | MV_UP   */
+       -1,             /*  7 => (invalid)          */
+       5,              /*  8 =>            MV_DOWN */
+       6,              /*  9 => MV_LEFT  | MV_DOWN */
+       4,              /* 10 => MV_RIGHT | MV_DOWN */
+       -1,             /* 11 => (invalid)          */
+       -1,             /* 12 => (invalid)          */
+       -1,             /* 13 => (invalid)          */
+       -1,             /* 14 => (invalid)          */
+       -1,             /* 15 => (invalid)          */
+      };
+      static struct
+      {
+       int dx, dy;
+       int dir;
+      } check_xy[8] =
+      {
+        { -1, -1,      MV_LEFT  | MV_UP   },
+               {  0, -1,                  MV_UP   },
+       { +1, -1,       MV_RIGHT | MV_UP   },
+       { +1,  0,       MV_RIGHT           },
+       { +1, +1,       MV_RIGHT | MV_DOWN },
+       {  0, +1,                  MV_DOWN },
+       { -1, +1,       MV_LEFT  | MV_DOWN },
+       { -1,  0,       MV_LEFT            },
+      };
+      int check_order = (RND(2) ? -1 : +1);
+      int start_pos = check_pos[MovDir[x][y] & 0x0f];
+      int i;
+
+      MovDelay[x][y] = level.android_move_time * 8 + 1;
+
+      if (start_pos < 0)       /* (should never happen) */
+       return;
+
+      for (i = 0; i < 3; i++)
+      {
+       /* first check start_pos, then previous/next or (next/previous) pos */
+       int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
+       int pos = (pos_raw + 8) % 8;
+       int newx = x + check_xy[pos].dx;
+       int newy = y + check_xy[pos].dy;
+       int new_move_dir = check_xy[pos].dir;
+
+       if (IS_PLAYER(newx, newy))
+         return;
+
+       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+       {
+         MovDir[x][y] = new_move_dir;
+
+         return;
+       }
+      }
+    }
   }
   else if (move_pattern == MV_TURNING_LEFT ||
           move_pattern == MV_TURNING_RIGHT ||
@@ -5007,7 +5178,7 @@ static void TurnRound(int x, int y)
     GfxFrame[x][y] = 0;
 
   if (MovDelay[x][y])
-    GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
+    GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -5532,7 +5703,7 @@ void StartMoving(int x, int y)
       }
       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
       {
-       if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
+       if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
          DrawLevelField(newx, newy);
        else
          GfxDir[x][y] = MovDir[x][y] = MV_NONE;
@@ -5573,6 +5744,75 @@ void StartMoving(int x, int y)
        return;
       }
     }
+    else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
+    {
+      if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL &&
+         ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+      {
+       int diagonal_move_dir = MovDir[x][y];
+       int change_delay = 8;
+       int graphic;
+
+       /* android is moving diagonally */
+
+       CreateField(x, y, EL_DIAGONAL_SHRINKING);
+
+       GfxElement[x][y] = EL_EMC_ANDROID;
+       GfxAction[x][y] = ACTION_SHRINKING;
+       GfxDir[x][y] = diagonal_move_dir;
+       ChangeDelay[x][y] = change_delay;
+
+       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
+                                GfxDir[x][y]);
+
+       DrawLevelGraphicAnimation(x, y, graphic);
+       PlayLevelSoundAction(x, y, ACTION_SHRINKING);
+
+#if 0
+       CheckTriggeredElementChangeBySide(x, y, element,
+                                         CE_MOVE_OF_X, new_move_dir);
+
+       TestIfElementTouchesCustomElement(x, y); /* empty or new element */
+
+       TestIfElementHitsCustomElement(newx, newy, new_move_dir);
+       TestIfElementTouchesCustomElement(newx, newy);
+#endif
+
+       CreateField(newx, newy, EL_DIAGONAL_GROWING);
+
+       Store[newx][newy] = EL_EMC_ANDROID;
+       GfxElement[newx][newy] = EL_EMC_ANDROID;
+       GfxAction[newx][newy] = ACTION_GROWING;
+       GfxDir[newx][newy] = diagonal_move_dir;
+       ChangeDelay[newx][newy] = change_delay;
+
+       graphic = el_act_dir2img(GfxElement[newx][newy], GfxAction[newx][newy],
+                                GfxDir[newx][newy]);
+
+       DrawLevelGraphicAnimation(newx, newy, graphic);
+       PlayLevelSoundAction(newx, newy, ACTION_GROWING);
+
+       return;
+      }
+      else if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+      {
+       Feld[newx][newy] = EL_EMPTY;
+       DrawLevelField(newx, newy);
+
+       PlayLevelSoundAction(x, y, ACTION_DIGGING);
+      }
+      else if (!IS_FREE(newx, newy))
+      {
+#if 0
+       if (IS_PLAYER(x, y))
+         DrawPlayerField(x, y);
+       else
+         DrawLevelField(x, y);
+#endif
+
+       return;
+      }
+    }
     else if (IS_CUSTOM_ELEMENT(element) &&
             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
     {
@@ -6074,6 +6314,9 @@ void ContinueMoving(int x, int y)
                                        player->index_bit, push_side);
   }
 
+  if (element == EL_EMC_ANDROID && pushed_by_player)   /* make another move */
+    MovDelay[newx][newy] = 1;
+
   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
 
   TestIfElementTouchesCustomElement(x, y);     /* empty or new element */
@@ -6351,6 +6594,7 @@ void AmoebeAbleger(int ax, int ay)
   int element = Feld[ax][ay];
   int graphic = el2img(element);
   int newax = ax, neway = ay;
+  boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -6359,7 +6603,7 @@ void AmoebeAbleger(int ax, int ay)
     { 0, +1 }
   };
 
-  if (!level.amoeba_speed)
+  if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
   {
     Feld[ax][ay] = EL_AMOEBA_DEAD;
     DrawLevelField(ax, ay);
@@ -6379,7 +6623,7 @@ void AmoebeAbleger(int ax, int ay)
       return;
   }
 
-  if (element == EL_AMOEBA_WET)        /* object is an acid / amoeba drop */
+  if (can_drop)                        /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
   {
     int start = RND(4);
     int x = ax + xy[start][0];
@@ -6473,13 +6717,13 @@ void AmoebeAbleger(int ax, int ay)
     }
   }
 
-  if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
+  if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
       (neway == lev_fieldy - 1 && newax != ax))
   {
     Feld[newax][neway] = EL_AMOEBA_GROWING;    /* creation of new amoeba */
     Store[newax][neway] = element;
   }
-  else if (neway == ay)
+  else if (neway == ay || element == EL_EMC_DRIPPER)
   {
     Feld[newax][neway] = EL_AMOEBA_DROP;       /* drop left/right of amoeba */
 
@@ -6647,6 +6891,13 @@ static void ActivateMagicBall(int bx, int by)
   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
 }
 
+static void InitDiagonalMovingElement(int x, int y)
+{
+#if 0
+  MovDelay[x][y] = level.android_move_time;
+#endif
+}
+
 void CheckExit(int x, int y)
 {
   if (local_player->gems_still_needed > 0 ||
@@ -7074,11 +7325,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      MV_NONE);
 
   int action_arg_number_min =
-    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
+    (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
      CA_ARG_MIN);
 
   int action_arg_number_max =
-    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
+    (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
      action_type == CA_SET_LEVEL_GEMS ? 999 :
      action_type == CA_SET_LEVEL_TIME ? 9999 :
      action_type == CA_SET_LEVEL_SCORE ? 99999 :
@@ -7087,7 +7338,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      CA_ARG_MAX);
 
   int action_arg_number_reset =
-    (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
+    (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
      action_type == CA_SET_LEVEL_TIME ? level.time :
      action_type == CA_SET_LEVEL_SCORE ? 0 :
@@ -7287,8 +7538,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
        {
          int move_stepsize = TILEX / stored_player[i].move_delay_value;
 
-         if (action_arg == CA_ARG_SPEED_SLOWER ||
-             action_arg == CA_ARG_SPEED_FASTER)
+         if (action_arg == CA_ARG_SPEED_FASTER &&
+             stored_player[i].cannot_move)
+         {
+           action_arg_number = STEPSIZE_VERY_SLOW;
+         }
+         else if (action_arg == CA_ARG_SPEED_SLOWER ||
+                  action_arg == CA_ARG_SPEED_FASTER)
          {
            action_arg_number = 2;
            action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
@@ -7302,6 +7558,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                                    action_arg_number_min,
                                    action_arg_number_max);
 
+#if 1
+         SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
+#else
          /* make sure that value is power of 2 */
          move_stepsize = (1 << log_2(move_stepsize));
 
@@ -7310,6 +7569,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
          stored_player[i].cannot_move =
            (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
+#endif
        }
       }
 
@@ -7633,6 +7893,9 @@ static boolean ChangeElement(int x, int y, int element, int page)
   {
     target_element = GET_TARGET_ELEMENT(change->target_element, change);
 
+    if (element == EL_DIAGONAL_GROWING)
+      target_element = Store[x][y];
+
     CreateElementFromChange(x, y, target_element);
 
     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
@@ -8292,9 +8555,20 @@ void GameActions()
 
   /* ---------- main game synchronization point ---------- */
 
+  WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+
   InitPlayfieldScanModeVars();
 
-  WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+#if USE_ONE_MORE_CHANGE_PER_FRAME
+  if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+  {
+    SCAN_PLAYFIELD(x, y)
+    {
+      ChangeCount[x][y] = 0;
+      ChangeEvent[x][y] = -1;
+    }
+  }
+#endif
 
   if (network_playing && !network_player_action_received)
   {
@@ -8475,7 +8749,7 @@ void GameActions()
     GfxFrame[x][y]++;
 
     /* reset finished pushing action (not done in ContinueMoving() to allow
-       continous pushing animation for elements with zero push delay) */
+       continuous pushing animation for elements with zero push delay) */
     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
     {
       ResetGfxAnimation(x, y);
@@ -8646,7 +8920,9 @@ void GameActions()
       CheckForDragon(x, y);
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (element == EL_ELEMENT_SNAPPING)
+    else if (element == EL_ELEMENT_SNAPPING ||
+            element == EL_DIAGONAL_SHRINKING ||
+            element == EL_DIAGONAL_GROWING)
     {
 #if 1
       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
@@ -9073,11 +9349,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 {
   int jx = player->jx, jy = player->jy;
   int new_jx = jx + dx, new_jy = jy + dy;
+#if !USE_FIXED_DONT_RUN_INTO
   int element;
+#endif
   int can_move;
+  boolean player_can_move = !player->cannot_move;
 
   if (!player->active || (!dx && !dy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
 
   player->MovDir = (dx < 0 ? MV_LEFT :
                    dx > 0 ? MV_RIGHT :
@@ -9085,9 +9364,9 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
                    dy > 0 ? MV_DOWN :  MV_NONE);
 
   if (!IN_LEV_FIELD(new_jx, new_jy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
 
-  if (player->cannot_move)
+  if (!player_can_move)
   {
 #if 1
     if (player->MovPos == 0)
@@ -9103,15 +9382,19 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
     SnapField(player, 0, 0);
 #endif
 
-    return MF_NO_ACTION;
+#if 0
+    return MP_NO_ACTION;
+#endif
   }
 
   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
 
+#if !USE_FIXED_DONT_RUN_INTO
   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
 
-  if (DONT_RUN_INTO(element))
+  /* (moved to DigField()) */
+  if (player_can_move && DONT_RUN_INTO(element))
   {
     if (element == EL_ACID && dx == 0 && dy == 1)
     {
@@ -9125,16 +9408,26 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
     else
       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
 
-    return MF_MOVING;
+    return MP_MOVING;
   }
+#endif
 
   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
-  if (can_move != MF_MOVING)
+#if 0
+#if USE_FIXED_DONT_RUN_INTO
+  if (can_move == MP_DONT_RUN_INTO)
+    return MP_MOVING;
+#endif
+#endif
+  if (can_move != MP_MOVING)
     return can_move;
 
+#if USE_FIXED_DONT_RUN_INTO
+#endif
+
   /* check if DigField() has caused relocation of the player */
   if (player->jx != jx || player->jy != jy)
-    return MF_NO_ACTION;       /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
+    return MP_NO_ACTION;       /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
 
   StorePlayer[jx][jy] = 0;
   player->last_jx = jx;
@@ -9158,14 +9451,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   ScrollPlayer(player, SCROLL_INIT);
 
-  return MF_MOVING;
+  return MP_MOVING;
 }
 
 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 {
   int jx = player->jx, jy = player->jy;
   int old_jx = jx, old_jy = jy;
-  int moved = MF_NO_ACTION;
+  int moved = MP_NO_ACTION;
 
   if (!player->active)
     return FALSE;
@@ -9238,7 +9531,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   jx = player->jx;
   jy = player->jy;
 
-  if (moved & MF_MOVING && !ScreenMovPos &&
+  if (moved & MP_MOVING && !ScreenMovPos &&
       (player == local_player || !options.network))
   {
     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
@@ -9309,7 +9602,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
   player->StepFrame = 0;
 
-  if (moved & MF_MOVING)
+  if (moved & MP_MOVING)
   {
     if (old_jx != jx && old_jy == jy)
       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
@@ -9817,6 +10110,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
+
   int bad_element = -1;
   static int test_xy[4][2] =
   {
@@ -10109,7 +10403,7 @@ void RemovePlayer(struct PlayerInfo *player)
 static void setFieldForSnapping(int x, int y, int element, int direction)
 {
   struct ElementInfo *ei = &element_info[element];
-  int direction_bit = MV_DIR_BIT(direction);
+  int direction_bit = MV_DIR_TO_BIT(direction);
   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
                IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
@@ -10169,6 +10463,8 @@ int DigField(struct PlayerInfo *player,
 {
   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
   boolean player_was_pushing = player->is_pushing;
+  boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
+  boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
   int jx = oldx, jy = oldy;
   int dx = x - jx, dy = y - jy;
   int nextx = x + dx, nexty = y + dy;
@@ -10179,7 +10475,11 @@ int DigField(struct PlayerInfo *player,
   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
   int dig_side = MV_DIR_OPPOSITE(move_direction);
   int old_element = Feld[jx][jy];
+#if USE_FIXED_DONT_RUN_INTO
+  int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
+#else
   int element;
+#endif
   int collect_count;
 
   if (is_player)               /* function can also be called by EL_PENGUIN */
@@ -10198,12 +10498,14 @@ int DigField(struct PlayerInfo *player,
       player->is_switching = FALSE;
       player->push_delay = -1;
 
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
     }
   }
 
+#if !USE_FIXED_DONT_RUN_INTO
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
+#endif
 
   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
@@ -10213,36 +10515,97 @@ int DigField(struct PlayerInfo *player,
           game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
+#if 0
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && DONT_RUN_INTO(element))
+  {
+    if (element == EL_ACID && dx == 0 && dy == 1)
+    {
+      SplashAcid(x, y);
+      Feld[jx][jy] = EL_PLAYER_1;
+      InitMovingField(jx, jy, MV_DOWN);
+      Store[jx][jy] = EL_ACID;
+      ContinueMoving(jx, jy);
+      BuryPlayer(player);
+    }
+    else
+      TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+#endif
+
+#if 1
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && DONT_RUN_INTO(element))
+  {
+    TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+#endif
+
   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
-    return MF_NO_ACTION;       /* field has no opening in this direction */
+    return MP_NO_ACTION;       /* field has no opening in this direction */
 
   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
-    return MF_NO_ACTION;       /* field has no opening in this direction */
-
-  element = Feld[x][y];
-#if USE_NEW_CUSTOM_VALUE
+    return MP_NO_ACTION;       /* field has no opening in this direction */
 
 #if 1
-  collect_count = element_info[element].collect_count_initial;
-#else
-  collect_count = CustomValue[x][y];
-#endif
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
+  {
+    SplashAcid(x, y);
+    Feld[jx][jy] = EL_PLAYER_1;
+    InitMovingField(jx, jy, MV_DOWN);
+    Store[jx][jy] = EL_ACID;
+    ContinueMoving(jx, jy);
+    BuryPlayer(player);
 
-#else
-  collect_count = element_info[element].collect_count_initial;
+    return MP_DONT_RUN_INTO;
+  }
+#endif
 #endif
 
 #if 0
-  if (element != EL_BLOCKED &&
-      CustomValue[x][y] != element_info[element].collect_count_initial)
-    printf("::: %d: %d != %d\n",
-          element,
-          CustomValue[x][y],
-          element_info[element].collect_count_initial);
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && DONT_RUN_INTO(element))
+  {
+    if (element == EL_ACID && dx == 0 && dy == 1)
+    {
+      SplashAcid(x, y);
+      Feld[jx][jy] = EL_PLAYER_1;
+      InitMovingField(jx, jy, MV_DOWN);
+      Store[jx][jy] = EL_ACID;
+      ContinueMoving(jx, jy);
+      BuryPlayer(player);
+    }
+    else
+      TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+#endif
+
+#if USE_FIXED_DONT_RUN_INTO
+  if (IS_MOVING(x, y) || IS_PLAYER(x, y))
+    return MP_NO_ACTION;
 #endif
 
+#if !USE_FIXED_DONT_RUN_INTO
+  element = Feld[x][y];
+#endif
+
+  collect_count = element_info[element].collect_count_initial;
+
   if (!is_player && !IS_COLLECTIBLE(element))  /* penguin cannot collect it */
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
+
+  if (game.engine_version < VERSION_IDENT(2,2,0,0))
+    player_can_move = player_can_move_or_snap;
 
   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
       game.engine_version >= VERSION_IDENT(2,2,0,0))
@@ -10253,17 +10616,18 @@ int DigField(struct PlayerInfo *player,
                                        player->index_bit, dig_side);
 
     if (Feld[x][y] != element)         /* field changed by snapping */
-      return MF_ACTION;
+      return MP_ACTION;
 
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
   }
 
   if (game.gravity && is_player && !player->is_auto_moving &&
       canFallDown(player) && move_direction != MV_DOWN &&
       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
-    return MF_NO_ACTION;       /* player cannot walk here due to gravity */
+    return MP_NO_ACTION;       /* player cannot walk here due to gravity */
 
-  if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+  if (player_can_move &&
+      IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
   {
     int sound_element = SND_ELEMENT(element);
     int sound_action = ACTION_WALKING;
@@ -10271,17 +10635,17 @@ int DigField(struct PlayerInfo *player,
     if (IS_RND_GATE(element))
     {
       if (!player->key[RND_GATE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_RND_GATE_GRAY(element))
     {
       if (!player->key[RND_GATE_GRAY_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_RND_GATE_GRAY_ACTIVE(element))
     {
       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (element == EL_EXIT_OPEN ||
             element == EL_SP_EXIT_OPEN ||
@@ -10300,28 +10664,29 @@ int DigField(struct PlayerInfo *player,
     else
       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
   }
-  else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+  else if (player_can_move &&
+          IS_PASSABLE(element) && canPassField(x, y, move_direction))
   {
     if (!ACCESS_FROM(element, opposite_direction))
-      return MF_NO_ACTION;     /* field not accessible from this direction */
+      return MP_NO_ACTION;     /* field not accessible from this direction */
 
     if (CAN_MOVE(element))     /* only fixed elements can be passed! */
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (IS_EM_GATE(element))
     {
       if (!player->key[EM_GATE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_EM_GATE_GRAY(element))
     {
       if (!player->key[EM_GATE_GRAY_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_EM_GATE_GRAY_ACTIVE(element))
     {
       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
-       return MF_NO_ACTION;
+       return MP_NO_ACTION;
     }
     else if (IS_SP_PORT(element))
     {
@@ -10354,7 +10719,7 @@ int DigField(struct PlayerInfo *player,
 
     PlayLevelSoundAction(x, y, ACTION_PASSING);
   }
-  else if (IS_DIGGABLE(element))
+  else if (player_can_move_or_snap && IS_DIGGABLE(element))
   {
     RemoveField(x, y);
 
@@ -10384,7 +10749,7 @@ int DigField(struct PlayerInfo *player,
                                          player->index_bit, dig_side);
     }
   }
-  else if (IS_COLLECTIBLE(element))
+  else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
   {
     RemoveField(x, y);
 
@@ -10498,17 +10863,17 @@ int DigField(struct PlayerInfo *player,
                                          player->index_bit, dig_side);
     }
   }
-  else if (IS_PUSHABLE(element))
+  else if (player_can_move_or_snap && IS_PUSHABLE(element))
   {
     if (mode == DF_SNAP && element != EL_BD_ROCK)
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (CAN_FALL(element) && dy)
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
        !(element == EL_SPRING && level.use_spring_bug))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
        ((move_direction & MV_VERTICAL &&
@@ -10521,12 +10886,12 @@ int DigField(struct PlayerInfo *player,
            IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
           (element_info[element].move_pattern & MV_DOWN &&
            IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     /* do not push elements already moving away faster than player */
     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
        ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
     {
@@ -10550,10 +10915,10 @@ int DigField(struct PlayerInfo *player,
          (IS_FREE(nextx, nexty) ||
           (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
            IS_SB_ELEMENT(element)))))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
 
     if (player->push_delay == -1)      /* new pushing; restart delay */
       player->push_delay = 0;
@@ -10566,7 +10931,7 @@ int DigField(struct PlayerInfo *player,
       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
        player->move_delay = 0;
 
-      return MF_NO_ACTION;
+      return MP_NO_ACTION;
     }
 
     if (IS_SB_ELEMENT(element))
@@ -10636,7 +11001,7 @@ int DigField(struct PlayerInfo *player,
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                          player->index_bit, dig_side);
 
-      return MF_ACTION;
+      return MP_ACTION;
     }
 
     player->is_switching = TRUE;
@@ -10763,7 +11128,7 @@ int DigField(struct PlayerInfo *player,
     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
-    return MF_ACTION;
+    return MP_ACTION;
   }
   else
   {
@@ -10789,7 +11154,7 @@ int DigField(struct PlayerInfo *player,
     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
   }
 
   player->push_delay = -1;
@@ -10800,7 +11165,7 @@ int DigField(struct PlayerInfo *player,
       player->is_collecting = !player->is_digging;
   }
 
-  return MF_MOVING;
+  return MP_MOVING;
 }
 
 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
@@ -10852,7 +11217,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 
   player->is_dropping = FALSE;
 
-  if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
+  if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
     return FALSE;
 
   player->is_snapping = TRUE;
@@ -10891,7 +11256,7 @@ boolean DropElement(struct PlayerInfo *player)
      element can be dropped (this is especially important if the next element
      is dynamite, which can be placed on background for historical reasons) */
   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
-    return MF_ACTION;
+    return MP_ACTION;
 
   if (IS_THROWABLE(drop_element))
   {