rnd-20060226-1-src
[rocksndiamonds.git] / src / game.c
index 0edf3ac4d1cdf89fd6e7f4c509d7586613ff66bc..64f521a9dd2c15cad2a6ea300bc779ed7d67c27c 100644 (file)
 #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_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
 #define USE_FIXED_DONT_RUN_INTO                (USE_NEW_STUFF          * 1)
+#define USE_NEW_SPRING_BUMPER          (USE_NEW_STUFF          * 1)
+#define USE_STOP_CHANGED_ELEMENTS      (USE_NEW_STUFF          * 1)
+#define USE_ELEMENT_TOUCHING_BUGFIX    (USE_NEW_STUFF          * 1)
+
+#define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
 
 /* for DigField() */
 #define DF_NO_PUSH             0
 #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 GET_CE_DELAY_VALUE(c)  (   ((c)->delay_fixed) + \
                                 RND((c)->delay_random))
 
+#if 1
+#define GET_VALID_RUNTIME_ELEMENT(e)                                   \
+        ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
+#else
+#define GET_VALID_FILE_ELEMENT(e)                                      \
+       ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
+#endif
+
 #define GET_TARGET_ELEMENT(e, ch)                                      \
-       ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
-        (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
+       ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player  :    \
+        (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element :    \
+        (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  : (e))
 
 #define CAN_GROW_INTO(e)                                               \
        ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
 #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 ANDROID_CAN_CLONE_FIELD(x, y)                                  \
+       (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
+                               CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
+
 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                 \
        ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
 
        (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
                                Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
 
+#if 0
 #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)
 #define IS_IN_GROUP_EL(e, ge)  (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
 
 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                    \
        (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
+#endif
+
+#define MOVE_ENTER_EL(e)       (element_info[e].move_enter_element)
 
 #define CE_ENTER_FIELD_COND(e, x, y)                                   \
                (!IS_PLAYER(x, y) &&                                    \
@@ -270,6 +294,8 @@ static void TestIfElementSmashesCustomElement(int, int, int);
 #endif
 
 static void HandleElementChange(int, int, int);
+static void ExecuteCustomElementAction(int, int, int, int);
+static boolean ChangeElement(int, int, int, int);
 
 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
 #define CheckTriggeredElementChange(x, y, e, ev)                       \
@@ -327,6 +353,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;
@@ -508,6 +536,22 @@ static struct ChangingElementInfo change_delay_list[] =
     NULL,
     NULL
   },
+  {
+    EL_DIAGONAL_SHRINKING,
+    EL_UNDEFINED,
+    0,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_DIAGONAL_GROWING,
+    EL_UNDEFINED,
+    0,
+    NULL,
+    NULL,
+    InitDiagonalMovingElement
+  },
 
   {
     EL_UNDEFINED,
@@ -943,6 +987,10 @@ static void InitField(int x, int y, boolean init_game)
       MovDelay[x][y] = 96;
       break;
 
+    case EL_EM_DYNAMITE_ACTIVE:
+      MovDelay[x][y] = 32;
+      break;
+
     case EL_LAMP:
       local_player->lights_still_needed++;
       break;
@@ -1203,6 +1251,7 @@ void DrawGameDoorValues()
     DrawGameValue_Keys(stored_player[i].key);
 }
 
+#if 0
 static void resolve_group_element(int group_element, int recursion_depth)
 {
   static int group_nr;
@@ -1246,7 +1295,7 @@ static void resolve_group_element(int group_element, int recursion_depth)
     }
   }
 }
-
+#endif
 
 /*
   =============================================================================
@@ -1378,6 +1427,7 @@ static void InitGameEngine()
   printf("       => game.engine_version == %06d\n", game.engine_version);
 #endif
 
+#if 0
   /* ---------- recursively resolve group elements ------------------------- */
 
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
@@ -1386,6 +1436,7 @@ static void InitGameEngine()
 
   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
     resolve_group_element(EL_GROUP_START + i, 0);
+#endif
 
   /* ---------- initialize player's initial move delay --------------------- */
 
@@ -1882,6 +1933,8 @@ void InitGame()
 
   game.envelope_active = FALSE;
 
+  game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
+
   for (i = 0; i < NUM_BELTS; i++)
   {
     game.belt_dir[i] = MV_NONE;
@@ -2046,7 +2099,7 @@ void InitGame()
 
   if (tape.playing)
   {
-    /* when playing a tape, eliminate all players which do not participate */
+    /* when playing a tape, eliminate all players who do not participate */
 
     for (i = 0; i < MAX_PLAYERS; i++)
     {
@@ -2610,6 +2663,13 @@ void GameWon()
 
   BackToFront();
 
+#if 0
+  if (tape.playing)
+    printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
+  else
+    printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
+#endif
+
   if (tape.playing)
     return;
 
@@ -2770,14 +2830,33 @@ static void ResetRandomAnimationValue(int x, int y)
 
 static void ResetGfxAnimation(int x, int y)
 {
+#if 0
+  int element, graphic;
+#endif
+
   GfxFrame[x][y] = 0;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MovDir[x][y];
+
+#if 0
+  element = Feld[x][y];
+  graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+  if (graphic_info[graphic].anim_global_sync)
+    GfxFrame[x][y] = FrameCounter;
+  else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+    GfxFrame[x][y] = CustomValue[x][y];
+  else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+    GfxFrame[x][y] = element_info[element].collect_score;
+#endif
 }
 
 void InitMovingField(int x, int y, int direction)
 {
   int element = Feld[x][y];
+#if 0
+  int graphic;
+#endif
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int newx = x + dx;
@@ -2791,6 +2870,17 @@ void InitMovingField(int x, int y, int direction)
   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
                     ACTION_FALLING : ACTION_MOVING);
 
+#if 0
+  graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+  if (graphic_info[graphic].anim_global_sync)
+    GfxFrame[x][y] = FrameCounter;
+  else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+    GfxFrame[x][y] = CustomValue[x][y];
+  else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+    GfxFrame[x][y] = element_info[element].collect_score;
+#endif
+
   /* this is needed for CEs with property "can move" / "not moving" */
 
   if (getElementMoveStepsize(x, y) != 0)       /* moving or being moved */
@@ -2814,8 +2904,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;
@@ -3002,7 +3097,7 @@ void CheckDynamite(int x, int y)
   Bang(x, y);
 }
 
-void DrawRelocatePlayer(struct PlayerInfo *player)
+void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
 {
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
@@ -3011,19 +3106,19 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
   int jx = player->jx;
   int jy = player->jy;
 
-  if (level.instant_relocation)
+  if (quick_relocation)
   {
     int offset = (setup.scroll_delay ? 3 : 0);
 
     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
     {
-      scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-                 local_player->jx - MIDPOSX);
+      scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
+                 player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+                 player->jx - MIDPOSX);
 
-      scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-                 local_player->jy - MIDPOSY);
+      scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+                 player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+                 player->jy - MIDPOSY);
     }
     else
     {
@@ -3057,13 +3152,13 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
       int dx = 0, dy = 0;
       int fx = FX, fy = FY;
 
-      scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-                  local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-                  local_player->jx - MIDPOSX);
+      scroll_xx = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
+                  player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+                  player->jx - MIDPOSX);
 
-      scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-                  local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-                  local_player->jy - MIDPOSY);
+      scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+                  player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+                  player->jy - MIDPOSY);
 
       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
@@ -3171,8 +3266,14 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
     InitField(jx, jy, FALSE);
   }
 
+#if 1
+  /* only visually relocate centered player */
+  if (player->index_nr == game.centered_player_nr)
+    DrawRelocatePlayer(player, level.instant_relocation);
+#else
   if (player == local_player)  /* only visually relocate local player */
-    DrawRelocatePlayer(player);
+    DrawRelocatePlayer(player, level.instant_relocation);
+#endif
 
   TestIfPlayerTouchesBadThing(jx, jy);
   TestIfPlayerTouchesCustomElement(jx, jy);
@@ -4664,7 +4765,7 @@ inline static void TurnRoundExt(int x, int y)
   }
   else if (element == EL_SPRING)
   {
-#if 1
+#if USE_NEW_SPRING_BUMPER
     if (MovDir[x][y] & MV_HORIZONTAL)
     {
       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
@@ -4691,7 +4792,8 @@ inline static void TurnRoundExt(int x, int y)
   }
   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;
 
@@ -4792,21 +4894,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;
 
@@ -4835,6 +4937,144 @@ 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 start_pos, check_order;
+      boolean can_clone = FALSE;
+      int i;
+
+      /* check if there is any free field around current position */
+      for (i = 0; i < 8; i++)
+      {
+       int newx = x + check_xy[i].dx;
+       int newy = y + check_xy[i].dy;
+
+       if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+       {
+         can_clone = TRUE;
+
+         break;
+       }
+      }
+
+      if (can_clone)           /* randomly find an element to clone */
+      {
+       can_clone = FALSE;
+
+       start_pos = check_pos[RND(8)];
+       check_order = (RND(2) ? -1 : +1);
+
+       for (i = 0; i < 8; i++)
+       {
+         int pos_raw = start_pos + i * check_order;
+         int pos = (pos_raw + 8) % 8;
+         int newx = x + check_xy[pos].dx;
+         int newy = y + check_xy[pos].dy;
+
+         if (ANDROID_CAN_CLONE_FIELD(newx, newy))
+         {
+           element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
+           element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
+
+           Store[x][y] = Feld[newx][newy];
+
+           can_clone = TRUE;
+
+           break;
+         }
+       }
+      }
+
+      if (can_clone)           /* randomly find a direction to move */
+      {
+       can_clone = FALSE;
+
+       start_pos = check_pos[RND(8)];
+       check_order = (RND(2) ? -1 : +1);
+
+       for (i = 0; i < 8; i++)
+       {
+         int pos_raw = start_pos + i * 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 (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
+         {
+           MovDir[x][y] = new_move_dir;
+           MovDelay[x][y] = level.android_clone_time * 8 + 1;
+
+           can_clone = TRUE;
+
+           break;
+         }
+       }
+      }
+
+      if (can_clone)           /* cloning and moving successful */
+       return;
+
+      /* cannot clone -- try to move towards player */
+
+      start_pos = check_pos[MovDir[x][y] & 0x0f];
+      check_order = (RND(2) ? -1 : +1);
+
+      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))
+         break;
+
+       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+       {
+         MovDir[x][y] = new_move_dir;
+         MovDelay[x][y] = level.android_move_time * 8 + 1;
+
+         break;
+       }
+      }
+    }
   }
   else if (move_pattern == MV_TURNING_LEFT ||
           move_pattern == MV_TURNING_RIGHT ||
@@ -5074,6 +5314,9 @@ inline static void TurnRoundExt(int x, int y)
 static void TurnRound(int x, int y)
 {
   int direction = MovDir[x][y];
+#if 1
+  int element, graphic;
+#endif
 
   TurnRoundExt(x, y);
 
@@ -5083,7 +5326,19 @@ 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);
+
+#if 1
+  element = Feld[x][y];
+  graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+  if (graphic_info[graphic].anim_global_sync)
+    GfxFrame[x][y] = FrameCounter;
+  else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+    GfxFrame[x][y] = CustomValue[x][y];
+  else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+    GfxFrame[x][y] = element_info[element].collect_score;
+#endif
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -5582,6 +5837,7 @@ void StartMoving(int x, int y)
 
     else if (CAN_MOVE_INTO_ACID(element) &&
             IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
+            !IS_MV_DIAGONAL(MovDir[x][y]) &&
             (MovDir[x][y] == MV_DOWN ||
              game.engine_version >= VERSION_IDENT(3,1,0,0)))
     {
@@ -5608,7 +5864,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;
@@ -5649,6 +5905,97 @@ void StartMoving(int x, int y)
        return;
       }
     }
+    else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
+    {
+      if (Store[x][y] != EL_EMPTY)
+      {
+       boolean can_clone = FALSE;
+       int xx, yy;
+
+       /* check if element to clone is still there */
+       for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
+       {
+         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
+         {
+           can_clone = TRUE;
+
+           break;
+         }
+       }
+
+       /* cannot clone or target field not free anymore -- do not clone */
+       if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+         Store[x][y] = EL_EMPTY;
+      }
+
+      if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
+      {
+       if (IS_MV_DIAGONAL(MovDir[x][y]))
+       {
+         int diagonal_move_dir = MovDir[x][y];
+         int stored = Store[x][y];
+         int change_delay = 8;
+         int graphic;
+
+         /* android is moving diagonally */
+
+         CreateField(x, y, EL_DIAGONAL_SHRINKING);
+
+         Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
+         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 (Feld[newx][newy] == EL_ACID)
+         {
+           SplashAcid(newx, newy);
+
+           return;
+         }
+
+         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
+       {
+         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))
     {
@@ -6050,16 +6397,28 @@ void ContinueMoving(int x, int y)
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
   /* some elements can leave other elements behind after moving */
+#if 1
+  if (ei->move_leave_element != EL_EMPTY &&
+      (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
+      (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#else
   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
+#endif
   {
     int move_leave_element = ei->move_leave_element;
 
 #if 1
+#if 1
+    /* this makes it possible to leave the removed element again */
+    if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
+      move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
+#else
     /* this makes it possible to leave the removed element again */
     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
       move_leave_element = stored;
+#endif
 #else
     /* this makes it possible to leave the removed element again */
     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
@@ -6150,10 +6509,35 @@ 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 */
 
+#if 0
+  if (ChangePage[newx][newy] != -1)            /* delayed change */
+  {
+    int page = ChangePage[newx][newy];
+    struct ElementChangeInfo *change = &ei->change_page[page];
+
+    ChangePage[newx][newy] = -1;
+
+    if (change->can_change)
+    {
+      if (ChangeElement(newx, newy, element, page))
+      {
+        if (change->post_change_function)
+          change->post_change_function(newx, newy);
+      }
+    }
+
+    if (change->has_action)
+      ExecuteCustomElementAction(newx, newy, element, page);
+  }
+#endif
+
   TestIfElementHitsCustomElement(newx, newy, direction);
   TestIfPlayerTouchesCustomElement(newx, newy);
   TestIfElementTouchesCustomElement(newx, newy);
@@ -6427,6 +6811,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 },
@@ -6435,7 +6820,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);
@@ -6455,7 +6840,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];
@@ -6549,13 +6934,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 */
 
@@ -6723,6 +7108,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 ||
@@ -7063,9 +7455,10 @@ static void WarnBuggyBase(int x, int y)
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
-    int xx = x + xy[i][0], yy = y + xy[i][1];
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
 
-    if (IS_PLAYER(xx, yy))
+    if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
     {
       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
 
@@ -7193,8 +7586,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
-     action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
-     action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
+     action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
+     action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+     action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
+     action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
      -1);
 
   int action_arg_number_old =
@@ -7485,6 +7880,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
        CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
        CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+
+#if 0
+       printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+#endif
       }
 #endif
 
@@ -7571,6 +7970,21 @@ static void CreateField(int x, int y, int element)
 
 static void CreateElementFromChange(int x, int y, int element)
 {
+  element = GET_VALID_RUNTIME_ELEMENT(element);
+
+#if USE_STOP_CHANGED_ELEMENTS
+  if (game.engine_version >= VERSION_IDENT(3,2,0,7))
+  {
+    int old_element = Feld[x][y];
+
+    /* prevent changed element from moving in same engine frame
+       unless both old and new element can either fall or move */
+    if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
+       (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
+      Stop[x][y] = TRUE;
+  }
+#endif
+
   CreateFieldExt(x, y, element, TRUE);
 }
 
@@ -7718,6 +8132,14 @@ 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 ||
+       element == EL_DIAGONAL_SHRINKING)
+    {
+      target_element = Store[x][y];
+
+      Store[x][y] = EL_EMPTY;
+    }
+
     CreateElementFromChange(x, y, target_element);
 
     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
@@ -8011,8 +8433,20 @@ static boolean CheckElementChangeExt(int x, int y,
     element = Feld[x][y];
   }
 
-  if (Feld[x][y] != element)   /* check if element has already changed */
+#if 0
+  /* check if element has already changed */
+  if (Feld[x][y] != element)
     return FALSE;
+#else
+  /* check if element has already changed or is about to change after moving */
+  if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
+       Feld[x][y] != element) ||
+
+      (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
+       (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
+       ChangePage[x][y] != -1)))
+    return FALSE;
+#endif
 
   for (p = 0; p < element_info[element].num_change_pages; p++)
   {
@@ -8381,6 +8815,27 @@ void GameActions()
 
   InitPlayfieldScanModeVars();
 
+  if (ScreenMovPos == 0)       /* screen currently aligned at tile position */
+  {
+    struct PlayerInfo *player;
+    int player_nr = game.centered_player_nr_next;
+
+    if (game.centered_player_nr_next == -1)
+      player_nr = local_player->index_nr;
+
+    player = &stored_player[player_nr];
+
+    if (!player->active)
+      game.centered_player_nr_next = game.centered_player_nr;
+
+    if (game.centered_player_nr != game.centered_player_nr_next)
+    {
+      DrawRelocatePlayer(player, setup.quick_switch);
+
+      game.centered_player_nr = game.centered_player_nr_next;
+    }
+  }
+
 #if USE_ONE_MORE_CHANGE_PER_FRAME
   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
   {
@@ -8742,7 +9197,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]);
@@ -9176,7 +9633,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
   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 :
@@ -9184,7 +9641,7 @@ 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_can_move)
   {
@@ -9203,12 +9660,18 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 #endif
 
 #if 0
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
 #endif
   }
 
+#if 1
+  if (!options.network && game.centered_player_nr == -1 &&
+      !AllPlayersInSight(player, new_jx, new_jy))
+    return MP_NO_ACTION;
+#else
   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
-    return MF_NO_ACTION;
+    return MP_NO_ACTION;
+#endif
 
 #if !USE_FIXED_DONT_RUN_INTO
   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
@@ -9228,17 +9691,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;
@@ -9262,14 +9734,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;
@@ -9342,8 +9814,14 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   jx = player->jx;
   jy = player->jy;
 
-  if (moved & MF_MOVING && !ScreenMovPos &&
+#if 1
+  if (moved & MP_MOVING && !ScreenMovPos &&
+      (player->index_nr == game.centered_player_nr ||
+       game.centered_player_nr == -1))
+#else
+  if (moved & MP_MOVING && !ScreenMovPos &&
       (player == local_player || !options.network))
+#endif
   {
     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
     int offset = (setup.scroll_delay ? 3 : 0);
@@ -9398,12 +9876,22 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
     {
+#if 1
+      if (!options.network && game.centered_player_nr == -1 &&
+         !AllPlayersInVisibleScreen())
+      {
+       scroll_x = old_scroll_x;
+       scroll_y = old_scroll_y;
+      }
+      else
+#else
       if (!options.network && !AllPlayersInVisibleScreen())
       {
        scroll_x = old_scroll_x;
        scroll_y = old_scroll_y;
       }
       else
+#endif
       {
        ScrollScreen(player, SCROLL_INIT);
        ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
@@ -9413,7 +9901,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);
@@ -9752,7 +10240,94 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
   }
 }
 
+#if USE_ELEMENT_TOUCHING_BUGFIX
+
 void TestIfElementTouchesCustomElement(int x, int y)
+{
+  static int xy[4][2] =
+  {
+    { 0, -1 },
+    { -1, 0 },
+    { +1, 0 },
+    { 0, +1 }
+  };
+  static int trigger_sides[4][2] =
+  {
+    /* center side     border side */
+    { CH_SIDE_TOP,     CH_SIDE_BOTTOM  },      /* check top    */
+    { CH_SIDE_LEFT,    CH_SIDE_RIGHT   },      /* check left   */
+    { CH_SIDE_RIGHT,   CH_SIDE_LEFT    },      /* check right  */
+    { CH_SIDE_BOTTOM,  CH_SIDE_TOP     }       /* check bottom */
+  };
+  static int touch_dir[4] =
+  {
+    MV_LEFT | MV_RIGHT,
+    MV_UP   | MV_DOWN,
+    MV_UP   | MV_DOWN,
+    MV_LEFT | MV_RIGHT
+  };
+  boolean change_center_element = FALSE;
+  int center_element = Feld[x][y];     /* should always be non-moving! */
+  int border_element_old[NUM_DIRECTIONS];
+  int i;
+
+  for (i = 0; i < NUM_DIRECTIONS; i++)
+  {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
+    int border_element;
+
+    border_element_old[i] = -1;
+
+    if (!IN_LEV_FIELD(xx, yy))
+      continue;
+
+    if (game.engine_version < VERSION_IDENT(3,0,7,0))
+      border_element = Feld[xx][yy];   /* may be moving! */
+    else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
+      border_element = Feld[xx][yy];
+    else if (MovDir[xx][yy] & touch_dir[i])    /* elements are touching */
+      border_element = MovingOrBlocked2Element(xx, yy);
+    else
+      continue;                        /* center and border element do not touch */
+
+    border_element_old[i] = border_element;
+  }
+
+  for (i = 0; i < NUM_DIRECTIONS; i++)
+  {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
+    int center_side = trigger_sides[i][0];
+    int border_element = border_element_old[i];
+
+    if (border_element == -1)
+      continue;
+
+    /* check for change of border element */
+    CheckElementChangeBySide(xx, yy, border_element, center_element,
+                            CE_TOUCHING_X, center_side);
+  }
+
+  for (i = 0; i < NUM_DIRECTIONS; i++)
+  {
+    int border_side = trigger_sides[i][1];
+    int border_element = border_element_old[i];
+
+    if (border_element == -1)
+      continue;
+
+    /* check for change of center element (but change it only once) */
+    if (!change_center_element)
+      change_center_element =
+       CheckElementChangeBySide(x, y, center_element, border_element,
+                                CE_TOUCHING_X, border_side);
+  }
+}
+
+#else
+
+void TestIfElementTouchesCustomElement_OLD(int x, int y)
 {
   static int xy[4][2] =
   {
@@ -9812,6 +10387,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
   }
 }
 
+#endif
+
 void TestIfElementHitsCustomElement(int x, int y, int direction)
 {
   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
@@ -10214,7 +10791,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);
@@ -10309,13 +10886,13 @@ 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))
@@ -10326,12 +10903,61 @@ 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 */
+    return MP_NO_ACTION;       /* field has no opening in this direction */
 
+#if 1
+#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);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+#endif
+
+#if 0
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
   {
@@ -10347,13 +10973,14 @@ int DigField(struct PlayerInfo *player,
     else
       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
 
-    return MF_NO_ACTION;
+    return MP_DONT_RUN_INTO;
   }
 #endif
+#endif
 
 #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 !USE_FIXED_DONT_RUN_INTO
@@ -10363,7 +10990,10 @@ int DigField(struct PlayerInfo *player,
   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))
@@ -10374,15 +11004,15 @@ 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 (player_can_move &&
       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
@@ -10393,17 +11023,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 ||
@@ -10426,25 +11056,25 @@ int DigField(struct PlayerInfo *player,
           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))
     {
@@ -10532,7 +11162,9 @@ int DigField(struct PlayerInfo *player,
       if (element == EL_SHIELD_DEADLY)
        player->shield_deadly_time_left += level.shield_deadly_time;
     }
-    else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
+    else if (element == EL_DYNAMITE ||
+            element == EL_EM_DYNAMITE ||
+            element == EL_SP_DISK_RED)
     {
       if (player->inventory_size < MAX_INVENTORY_SIZE)
        player->inventory_element[player->inventory_size++] = element;
@@ -10624,14 +11256,14 @@ int DigField(struct PlayerInfo *player,
   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 &&
@@ -10644,12 +11276,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))
     {
@@ -10673,10 +11305,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;
@@ -10689,7 +11321,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))
@@ -10759,7 +11391,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;
@@ -10886,7 +11518,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
   {
@@ -10912,7 +11544,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;
@@ -10923,7 +11555,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)
@@ -10975,7 +11607,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;
@@ -11014,7 +11646,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))
   {
@@ -11061,6 +11693,8 @@ boolean DropElement(struct PlayerInfo *player)
 
       if (new_element == EL_DYNAMITE)
        new_element = EL_DYNAMITE_ACTIVE;
+      else if (new_element == EL_EM_DYNAMITE)
+       new_element = EL_EM_DYNAMITE_ACTIVE;
       else if (new_element == EL_SP_DISK_RED)
        new_element = EL_SP_DISK_RED_ACTIVE;
     }
@@ -11292,7 +11926,7 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
       break;
 
     case SAMPLE_slurp:
-      PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
+      PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
       break;
 
     case SAMPLE_eater:
@@ -11454,6 +12088,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_NUT]);
       break;
     case EL_DYNAMITE:
+    case EL_EM_DYNAMITE:
     case EL_SP_DISK_RED:
     case EL_DYNABOMB_INCREASE_NUMBER:
     case EL_DYNABOMB_INCREASE_SIZE: