rnd-20060102-2-src
[rocksndiamonds.git] / src / game.c
index 4d67b93151c841f642ef47749dda96984093b649..88f19060e27be07027db1f802a268efe382fe76f 100644 (file)
@@ -34,6 +34,7 @@
 #define USE_NEW_PLAYER_SPEED           (USE_NEW_STUFF          * 1)
 #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          * 0)
 
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define SCROLL_INIT            0
 #define SCROLL_GO_ON           1
 
-/* for Explode() */
+/* for Bang()/Explode() */
 #define EX_PHASE_START         0
 #define EX_TYPE_NONE           0
 #define EX_TYPE_NORMAL         (1 << 0)
 #define EX_TYPE_CENTER         (1 << 1)
 #define EX_TYPE_BORDER         (1 << 2)
 #define EX_TYPE_CROSS          (1 << 3)
+#define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
 /* special positions in the game control window (relative to control window) */
                                 RND(element_info[e].ce_value_random_initial))
 #define GET_CHANGE_DELAY(c)    (   ((c)->delay_fixed  * (c)->delay_frames) + \
                                 RND((c)->delay_random * (c)->delay_frames))
+#define GET_CE_DELAY_VALUE(c)  (   ((c)->delay_fixed) + \
+                                RND((c)->delay_random))
 
 #define GET_TARGET_ELEMENT(e, ch)                                      \
        ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
@@ -1137,7 +1141,7 @@ static void resolve_group_element(int group_element, int recursion_depth)
 
 static void InitGameEngine()
 {
-  int i, j, k, l;
+  int i, j, k, l, x, y;
 
   /* set game engine from tape file when re-playing, else from level file */
   game.engine_version = (tape.playing ? tape.engine_version :
@@ -1209,6 +1213,38 @@ static void InitGameEngine()
   game.use_block_last_field_bug =
     (game.engine_version < VERSION_IDENT(3,1,1,0));
 
+  /*
+    Summary of bugfix/change:
+    Changed behaviour of CE changes with multiple changes per single frame.
+
+    Fixed/changed in version:
+    3.2.0-6
+
+    Description:
+    Before 3.2.0-6, only one single CE change was allowed in each engine frame.
+    This resulted in race conditions where CEs seem to behave strange in some
+    situations (where triggered CE changes were just skipped because there was
+    already a CE change on that tile in the playfield in that engine frame).
+    Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
+    (The number of changes per frame must be limited in any case, because else
+    it is easily possible to define CE changes that would result in an infinite
+    loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
+    should be set large enough so that it would only be reached in cases where
+    the corresponding CE change conditions run into a loop. Therefore, it seems
+    to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
+    maximal number of change pages for custom elements.)
+
+    Affected levels/tapes:
+    Probably many.
+  */
+
+#if USE_ONLY_ONE_CHANGE_PER_FRAME
+  game.max_num_changes_per_frame = 1;
+#else
+  game.max_num_changes_per_frame =
+    (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
+#endif
+
   /* ---------------------------------------------------------------------- */
 
   /* dynamically adjust element properties according to game engine version */
@@ -1472,6 +1508,39 @@ static void InitGameEngine()
   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
     element_info[access_direction_list[i].element].access_direction =
       access_direction_list[i].direction;
+
+  /* ---------- initialize explosion content ------------------------------- */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    if (IS_CUSTOM_ELEMENT(i))
+      continue;
+
+    for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+    {
+      /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
+
+      element_info[i].content.e[x][y] =
+       (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
+        i == EL_PLAYER_2 ? EL_EMERALD_RED :
+        i == EL_PLAYER_3 ? EL_EMERALD :
+        i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
+        i == EL_MOLE ? EL_EMERALD_RED :
+        i == EL_PENGUIN ? EL_EMERALD_PURPLE :
+        i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
+        i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
+        i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
+        i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
+        i == EL_WALL_EMERALD ? EL_EMERALD :
+        i == EL_WALL_DIAMOND ? EL_DIAMOND :
+        i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
+        i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
+        i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
+        i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
+        i == EL_WALL_PEARL ? EL_PEARL :
+        i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
+        EL_EMPTY);
+    }
+  }
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -1555,7 +1624,9 @@ void InitGame()
     player->StepFrame = 0;
 
     player->use_murphy = FALSE;
-    player->artwork_element = player->element_nr;
+    player->artwork_element =
+      (level.use_artwork_element[i] ? level.artwork_element[i] :
+       player->element_nr);
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
@@ -1706,7 +1777,7 @@ void InitGame()
       Stop[x][y] = FALSE;
       Pushed[x][y] = FALSE;
 
-      Changed[x][y] = FALSE;
+      Changed[x][y] = 0;
       ChangeEvent[x][y] = -1;
 
       ExplodePhase[x][y] = 0;
@@ -2993,6 +3064,21 @@ void Explode(int ex, int ey, int phase, int mode)
   if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
     int center_element = Feld[ex][ey];
+    int artwork_element = center_element;      /* for custom player artwork */
+    int explosion_element = center_element;    /* for custom player artwork */
+
+    if (IS_PLAYER(ex, ey))
+    {
+      int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
+
+      artwork_element = stored_player[player_nr].artwork_element;
+
+      if (level.use_explosion_element[player_nr])
+      {
+       explosion_element = level.explosion_element[player_nr];
+       artwork_element = explosion_element;
+      }
+    }
 
 #if 0
     /* --- This is only really needed (and now handled) in "Impact()". --- */
@@ -3006,7 +3092,7 @@ void Explode(int ex, int ey, int phase, int mode)
     if (mode == EX_TYPE_NORMAL ||
        mode == EX_TYPE_CENTER ||
        mode == EX_TYPE_CROSS)
-      PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
+      PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
 
     /* remove things displayed in background while burning dynamite */
     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
@@ -3020,7 +3106,7 @@ void Explode(int ex, int ey, int phase, int mode)
       Feld[ex][ey] = center_element;
     }
 
-    last_phase = element_info[center_element].explosion_delay + 1;
+    last_phase = element_info[explosion_element].explosion_delay + 1;
 
     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
     {
@@ -3093,6 +3179,11 @@ void Explode(int ex, int ey, int phase, int mode)
 
       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
       {
+       int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
+
+       Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
+
+#if 0
        switch(StorePlayer[ex][ey])
        {
          case EL_PLAYER_2:
@@ -3109,10 +3200,29 @@ void Explode(int ex, int ey, int phase, int mode)
            Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
            break;
        }
+#endif
 
        if (PLAYERINFO(ex, ey)->use_murphy)
          Store[x][y] = EL_EMPTY;
       }
+#if 1
+      else if (center_element == EL_YAMYAM)
+       Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
+      else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
+       Store[x][y] = element_info[center_element].content.e[xx][yy];
+#if 1
+      /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
+        (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
+        otherwise) -- FIX THIS !!! */
+      else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
+       Store[x][y] = element_info[element].content.e[1][1];
+#else
+      else if (!CAN_EXPLODE(element))
+       Store[x][y] = element_info[element].content.e[1][1];
+#endif
+      else
+       Store[x][y] = EL_EMPTY;
+#else
       else if (center_element == EL_MOLE)
        Store[x][y] = EL_EMERALD_RED;
       else if (center_element == EL_PENGUIN)
@@ -3150,13 +3260,14 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = element_info[element].content.e[1][1];
       else
        Store[x][y] = EL_EMPTY;
+#endif
 
       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
          center_element == EL_AMOEBA_TO_DIAMOND)
        Store2[x][y] = element;
 
       Feld[x][y] = EL_EXPLOSION;
-      GfxElement[x][y] = center_element;
+      GfxElement[x][y] = artwork_element;
 
       ExplodePhase[x][y] = 1;
       ExplodeDelay[x][y] = last_phase;
@@ -3250,12 +3361,30 @@ void Explode(int ex, int ey, int phase, int mode)
     /* player can escape from explosions and might therefore be still alive */
     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
        element <= EL_PLAYER_IS_EXPLODING_4)
-      Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
-                   EL_EMPTY :
-                   element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
-                   element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
-                   element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
-                   EL_EMERALD_PURPLE);
+    {
+      static int player_death_elements[] =
+      {
+       EL_EMERALD_YELLOW,
+       EL_EMERALD_RED,
+       EL_EMERALD,
+       EL_EMERALD_PURPLE
+      };
+      int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
+      int player_death_element = player_death_elements[player_nr];
+
+      if (level.use_explosion_element[player_nr])
+      {
+       int explosion_element = level.explosion_element[player_nr];
+       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
+       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
+
+       player_death_element =
+         element_info[explosion_element].content.e[xx][yy];
+      }
+
+      Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
+                   player_death_element);
+    }
 
     /* restore probably existing indestructible background element */
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
@@ -3363,6 +3492,7 @@ void DynaExplode(int ex, int ey)
 void Bang(int x, int y)
 {
   int element = MovingOrBlocked2Element(x, y);
+  int explosion_type = EX_TYPE_NORMAL;
 
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
   {
@@ -3370,6 +3500,16 @@ void Bang(int x, int y)
 
     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
                            player->element_nr);
+
+    if (level.use_explosion_element[player->index_nr])
+    {
+      int explosion_element = level.explosion_element[player->index_nr];
+
+      if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
+       explosion_type = EX_TYPE_CROSS;
+      else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
+       explosion_type = EX_TYPE_CENTER;
+    }
   }
 
   switch(element)
@@ -3384,8 +3524,8 @@ void Bang(int x, int y)
     case EL_PACMAN:
     case EL_MOLE:
       RaiseScoreElement(element);
-      Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
       break;
+
     case EL_DYNABOMB_PLAYER_1_ACTIVE:
     case EL_DYNABOMB_PLAYER_2_ACTIVE:
     case EL_DYNABOMB_PLAYER_3_ACTIVE:
@@ -3393,27 +3533,30 @@ void Bang(int x, int y)
     case EL_DYNABOMB_INCREASE_NUMBER:
     case EL_DYNABOMB_INCREASE_SIZE:
     case EL_DYNABOMB_INCREASE_POWER:
-      DynaExplode(x, y);
+      explosion_type = EX_TYPE_DYNA;
       break;
+
     case EL_PENGUIN:
     case EL_LAMP:
     case EL_LAMP_ACTIVE:
     case EL_AMOEBA_TO_DIAMOND:
-      if (IS_PLAYER(x, y))
-       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
-      else
-       Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
+      if (!IS_PLAYER(x, y))    /* penguin and player may be at same field */
+       explosion_type = EX_TYPE_CENTER;
       break;
+
     default:
       if (element_info[element].explosion_type == EXPLODES_CROSS)
-       Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
+       explosion_type = EX_TYPE_CROSS;
       else if (element_info[element].explosion_type == EXPLODES_1X1)
-       Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
-      else
-       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
+       explosion_type = EX_TYPE_CENTER;
       break;
   }
 
+  if (explosion_type == EX_TYPE_DYNA)
+    DynaExplode(x, y);
+  else
+    Explode(x, y, EX_PHASE_START, explosion_type);
+
   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
 }
 
@@ -4081,7 +4224,7 @@ inline static void TurnRoundExt(int x, int y)
 {
   static struct
   {
-    int x, y;
+    int dx, dy;
   } move_xy[] =
   {
     {  0,  0 },
@@ -4116,10 +4259,10 @@ inline static void TurnRoundExt(int x, int y)
   int right_dir = turn[old_move_dir].right;
   int back_dir  = turn[old_move_dir].back;
 
-  int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
-  int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
-  int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
-  int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
+  int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
+  int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
+  int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
+  int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
 
   int left_x  = x + left_dx,  left_y  = y + left_dy;
   int right_x = x + right_dx, right_y = y + right_dy;
@@ -4272,8 +4415,8 @@ inline static void TurnRoundExt(int x, int y)
     else
       MovDir[x][y] = back_dir;
 
-    xx = x + move_xy[MovDir[x][y]].x;
-    yy = y + move_xy[MovDir[x][y]].y;
+    xx = x + move_xy[MovDir[x][y]].dx;
+    yy = y + move_xy[MovDir[x][y]].dy;
 
     if (!IN_LEV_FIELD(xx, yy) ||
         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
@@ -4300,8 +4443,8 @@ inline static void TurnRoundExt(int x, int y)
     else
       MovDir[x][y] = back_dir;
 
-    xx = x + move_xy[MovDir[x][y]].x;
-    yy = y + move_xy[MovDir[x][y]].y;
+    xx = x + move_xy[MovDir[x][y]].dx;
+    yy = y + move_xy[MovDir[x][y]].dy;
 
     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
       MovDir[x][y] = old_move_dir;
@@ -5660,7 +5803,11 @@ void ContinueMoving(int x, int y)
 
   MovDelay[newx][newy] = 0;
 
+#if 1
+  if (CAN_CHANGE_OR_HAS_ACTION(element))
+#else
   if (CAN_CHANGE(element))
+#endif
   {
     /* copy element change control values to new field */
     ChangeDelay[newx][newy] = ChangeDelay[x][y];
@@ -5675,7 +5822,7 @@ void ContinueMoving(int x, int y)
 
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
-  Changed[x][y] = FALSE;
+  Changed[x][y] = 0;
   ChangeEvent[x][y] = -1;
 
 #if USE_NEW_CUSTOM_VALUE
@@ -6771,7 +6918,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 #else
      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
 #endif
-     action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
+     action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
      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 :
@@ -7009,7 +7156,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          int artwork_element = action_arg_element;
 
          if (action_arg == CA_ARG_ELEMENT_RESET)
-           artwork_element = stored_player[i].element_nr;
+           artwork_element =
+             (level.use_artwork_element[i] ? level.artwork_element[i] :
+              stored_player[i].element_nr);
 
          stored_player[i].artwork_element = artwork_element;
 
@@ -7118,11 +7267,7 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change,
   if (ELEM_IS_PLAYER(target_element))
     RelocatePlayer(x, y, target_element);
 
-#if 1
-  Changed[x][y] = TRUE;                /* ignore all further changes in this frame */
-#else
-  Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
-#endif
+  Changed[x][y]++;             /* count number of changes in the same frame */
 
   TestIfBadThingTouchesPlayer(x, y);
   TestIfPlayerTouchesCustomElement(x, y);
@@ -7148,21 +7293,11 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     change->actual_trigger_ce_value = 0;
   }
 
-#if 1
-  /* do not change any elements that have already changed in this frame */
-  if (Changed[x][y])
-    return FALSE;
-#else
-  /* do not change already changed elements with same change event */
-  if (Changed[x][y] & ChangeEvent[x][y])
+  /* do not change elements more than a specified maximum number of changes */
+  if (Changed[x][y] >= game.max_num_changes_per_frame)
     return FALSE;
-#endif
 
-#if 1
-  Changed[x][y] = TRUE;                /* ignore all further changes in this frame */
-#else
-  Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
-#endif
+  Changed[x][y]++;             /* count number of changes in the same frame */
 
   if (change->explode)
   {
@@ -7595,6 +7730,29 @@ static boolean CheckElementChangeExt(int x, int y,
       change->actual_trigger_side = trigger_side;
       change->actual_trigger_ce_value = CustomValue[x][y];
 
+      /* special case: trigger element not at (x,y) position for some events */
+      if (check_trigger_element)
+      {
+       static struct
+       {
+         int dx, dy;
+       } move_xy[] =
+         {
+           {  0,  0 },
+           { -1,  0 },
+           { +1,  0 },
+           {  0,  0 },
+           {  0, -1 },
+           {  0,  0 }, { 0, 0 }, { 0, 0 },
+           {  0, +1 }
+         };
+
+       int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
+       int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
+
+       change->actual_trigger_ce_value = CustomValue[xx][yy];
+      }
+
       if (change->can_change && !change_done)
       {
        ChangeDelay[x][y] = 1;
@@ -8044,7 +8202,7 @@ void GameActions()
 
   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
   {
-    Changed[x][y] = FALSE;
+    Changed[x][y] = 0;
     ChangeEvent[x][y] = -1;
 
     /* this must be handled before main playfield loop */
@@ -8121,6 +8279,28 @@ void GameActions()
 
     if (graphic_info[graphic].anim_global_sync)
       GfxFrame[x][y] = FrameCounter;
+    else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+    {
+      int old_gfx_frame = GfxFrame[x][y];
+
+      GfxFrame[x][y] = CustomValue[x][y];
+
+#if 1
+      if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+       DrawLevelGraphicAnimation(x, y, graphic);
+    }
+    else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+    {
+      int old_gfx_frame = GfxFrame[x][y];
+
+      GfxFrame[x][y] = element_info[element].collect_score;
+
+#if 1
+      if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+       DrawLevelGraphicAnimation(x, y, graphic);
+    }
 
     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
        IS_NEXT_FRAME(GfxFrame[x][y], graphic))
@@ -8151,6 +8331,11 @@ void GameActions()
       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
 #endif
 
+#if 0
+      if (element == EL_CUSTOM_255)
+       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
+#endif
+
 #if 1
       ChangeElement(x, y, page);
 #else
@@ -8234,6 +8419,12 @@ void GameActions()
     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#if 0
+    if (element == EL_CUSTOM_255 ||
+       element == EL_CUSTOM_256)
+      DrawLevelGraphicAnimation(x, y, graphic);
+#endif
+
     if (IS_BELT_ACTIVE(element))
       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
 
@@ -8652,8 +8843,19 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   if (player->cannot_move)
   {
+#if 1
+    if (player->MovPos == 0)
+    {
+      player->is_moving = FALSE;
+      player->is_digging = FALSE;
+      player->is_collecting = FALSE;
+      player->is_snapping = FALSE;
+      player->is_pushing = FALSE;
+    }
+#else
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
+#endif
 
     return MF_NO_ACTION;
   }
@@ -10468,7 +10670,7 @@ boolean DropElement(struct PlayerInfo *player)
     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
 
     /* needed if previous element just changed to "empty" in the last frame */
-    Changed[dropx][dropy] = FALSE;             /* allow another change */
+    Changed[dropx][dropy] = 0;         /* allow at least one more change */
 
     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
                               player->index_bit, drop_side);
@@ -10508,7 +10710,7 @@ boolean DropElement(struct PlayerInfo *player)
     nextx = dropx + GET_DX_FROM_DIR(move_direction);
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
 
-    Changed[dropx][dropy] = FALSE;             /* allow another change */
+    Changed[dropx][dropy] = 0;         /* allow at least one more change */
     CheckCollision[dropx][dropy] = 2;
   }