rnd-20050626-1-src
[rocksndiamonds.git] / src / game.c
index bac83725cb81b334bd9511d71ef56a865744b23d..ed511ad661728b37f43cddf69a21d001dad8e42c 100644 (file)
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF          (TRUE                           * 1)
+#define USE_NEW_STUFF                  (TRUE                           * 1)
 
-#define USE_NEW_MOVE_STYLE     (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_MOVE_DELAY     (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_PUSH_DELAY     (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_BLOCK_STYLE    (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_SP_SLIPPERY    (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_RANDOMIZE      (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_MOVE_STYLE             (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_MOVE_DELAY             (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_PUSH_DELAY             (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_BLOCK_STYLE            (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_SP_SLIPPERY            (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_RANDOMIZE              (TRUE   * USE_NEW_STUFF         * 1)
 
-#define USE_PUSH_BUGFIX                (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_CAN_MOVE_NOT_MOVING                (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_PREVIOUS_MOVE_DIR          (TRUE   * USE_NEW_STUFF         * 1)
 
-#define USE_CAN_MOVE_NOT_MOVING        (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_PREVIOUS_MOVE_DIR  (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_PUSH_BUGFIX                        (TRUE   * USE_NEW_STUFF         * 1)
+#if 0
+#define USE_BLOCK_DELAY_BUGFIX         (TRUE   * USE_NEW_STUFF         * 1)
+#endif
+#define USE_GRAVITY_BUGFIX_NEW         (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_GRAVITY_BUGFIX_OLD         (TRUE   * USE_NEW_STUFF         * 0)
+
+#define USE_PENGUIN_COLLECT_BUGFIX     (TRUE   * USE_NEW_STUFF         * 1)
+
+#define USE_IMPACT_BUGFIX              (TRUE   * USE_NEW_STUFF         * 1)
 
 
 /* for DigField() */
@@ -665,10 +674,9 @@ access_direction_list[] =
   { EL_UNDEFINED,                      MV_NO_MOVING                         }
 };
 
-static unsigned long trigger_events[MAX_NUM_ELEMENTS];
+static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
 
-#define IS_AUTO_CHANGING(e)    (element_info[e].change_events & \
-                                CH_EVENT_BIT(CE_DELAY))
+#define IS_AUTO_CHANGING(e)    (element_info[e].has_change_event[CE_DELAY])
 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
 #define IS_CHANGING(x, y)      (IS_AUTO_CHANGING(Feld[x][y]) || \
                                 IS_JUST_CHANGING(x, y))
@@ -780,6 +788,45 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                                level.block_last_field);
 
 #if USE_NEW_BLOCK_STYLE
+#if 1
+
+    /* ---------- initialize player's last field block delay --------------- */
+
+    /* always start with reliable default value (no adjustment needed) */
+    player->block_delay_adjustment = 0;
+
+    /* special case 1: in Supaplex, Murphy blocks last field one more frame */
+    if (player->block_last_field && element == EL_SP_MURPHY)
+      player->block_delay_adjustment = 1;
+
+    /* special case 2: in game engines before 3.1.1, blocking was different */
+    if (game.use_block_last_field_bug)
+      player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
+
+#if 0
+    /* blocking the last field when moving was corrected in version 3.1.1 */
+    if (game.use_block_last_field_bug)
+    {
+      /* even "not blocking" was blocking the last field for one frame */
+      level.block_delay    = (level.block_last_field    ? 7 : 1);
+      level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
+
+      level.block_last_field = TRUE;
+      level.sp_block_last_field = TRUE;
+    }
+#endif
+
+#if 0  /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
+    level.block_delay = 8;             /* when blocking, block 8 frames */
+    level.sp_block_delay = 9;          /* SP indeed blocks 9 frames, not 8 */
+#endif
+
+#if 0
+    printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
+#endif
+
+#else
+
 #if 1
     player->block_delay = (player->block_last_field ?
                           (element == EL_SP_MURPHY ?
@@ -791,6 +838,8 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                           (player->block_last_field ? 7 : 1));
 #endif
 
+#endif
+
 #if 0
     printf("::: block_last_field == %d, block_delay = %d\n",
           player->block_last_field, player->block_delay);
@@ -1221,7 +1270,7 @@ static void resolve_group_element(int group_element, int recursion_depth)
 
 static void InitGameEngine()
 {
-  int i, j, k;
+  int i, j, k, l;
 
   /* set game engine from tape file when re-playing, else from level file */
   game.engine_version = (tape.playing ? tape.engine_version :
@@ -1232,7 +1281,13 @@ static void InitGameEngine()
   /* ---------------------------------------------------------------------- */
 
   /*
-    Type of bug/change:
+    Summary of bugfix/change:
+    Fixed handling for custom elements that change when pushed by the player.
+
+    Fixed/changed in version:
+    3.1.0
+
+    Description:
     Before 3.1.0, custom elements that "change when pushing" changed directly
     after the player started pushing them (until then handled in "DigField()").
     Since 3.1.0, these custom elements are not changed until the "pushing"
@@ -1253,12 +1308,40 @@ static void InitGameEngine()
     Machine" by Juergen Bonhagen.
   */
 
-  game.use_bug_change_when_pushing =
+  game.use_change_when_pushing_bug =
     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
      !(tape.playing &&
        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
        tape.game_version <  VERSION_IDENT(3,1,1,0)));
 
+  /*
+    Summary of bugfix/change:
+    Fixed handling for blocking the field the player leaves when moving.
+
+    Fixed/changed in version:
+    3.1.1
+
+    Description:
+    Before 3.1.1, when "block last field when moving" was enabled, the field
+    the player is leaving when moving was blocked for the time of the move,
+    and was directly unblocked afterwards. This resulted in the last field
+    being blocked for exactly one less than the number of frames of one player
+    move. Additionally, even when blocking was disabled, the last field was
+    blocked for exactly one frame.
+    Since 3.1.1, due to changes in player movement handling, the last field
+    is not blocked at all when blocking is disabled. When blocking is enabled,
+    the last field is blocked for exactly the number of frames of one player
+    move. Additionally, if the player is Murphy, the hero of Supaplex, the
+    last field is blocked for exactly one more than the number of frames of
+    one player move.
+
+    Affected levels/tapes:
+    (!!! yet to be determined -- probably many !!!)
+  */
+
+  game.use_block_last_field_bug =
+    (game.engine_version < VERSION_IDENT(3,1,1,0));
+
   /* ---------------------------------------------------------------------- */
 
   /* dynamically adjust element properties according to game engine version */
@@ -1326,9 +1409,10 @@ static void InitGameEngine()
       ei->change->delay_frames = 1;
     }
 
-    ei->change_events = CE_BITMASK_DEFAULT;
     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
     {
+      ei->has_change_event[j] = FALSE;
+
       ei->event_page_nr[j] = 0;
       ei->event_page[j] = &ei->change_page[0];
     }
@@ -1347,7 +1431,7 @@ static void InitGameEngine()
     ei->change->change_function      = ch_delay->change_function;
     ei->change->post_change_function = ch_delay->post_change_function;
 
-    ei->change_events |= CH_EVENT_BIT(CE_DELAY);
+    ei->has_change_event[CE_DELAY] = TRUE;
 
 #if 1
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
@@ -1368,10 +1452,10 @@ static void InitGameEngine()
       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
       {
        /* only add event page for the first page found with this event */
-       if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
-           !(ei->change_events & CH_EVENT_BIT(k)))
+       if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
        {
-         ei->change_events |= CH_EVENT_BIT(k);
+         ei->has_change_event[k] = TRUE;
+
          ei->event_page_nr[k] = j;
          ei->event_page[k] = &ei->change_page[j];
        }
@@ -1388,7 +1472,7 @@ static void InitGameEngine()
 
     /* only add custom elements that change after fixed/random frame delay */
     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
-      element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
+      element_info[element].has_change_event[CE_DELAY] = TRUE;
   }
 #endif
 
@@ -1409,7 +1493,8 @@ static void InitGameEngine()
 
   /* initialize trigger events information */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
-    trigger_events[i] = EP_BITMASK_DEFAULT;
+    for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+      trigger_events[i][j] = FALSE;
 
 #if 1
   /* add trigger events from element change event properties */
@@ -1422,20 +1507,26 @@ static void InitGameEngine()
       if (!ei->change_page[j].can_change)
        continue;
 
-      if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
+      if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
       {
        int trigger_element = ei->change_page[j].trigger_element;
 
-       if (IS_GROUP_ELEMENT(trigger_element))
+       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
        {
-         struct ElementGroupInfo *group = element_info[trigger_element].group;
+         if (ei->change_page[j].has_event[k])
+         {
+           if (IS_GROUP_ELEMENT(trigger_element))
+           {
+             struct ElementGroupInfo *group =
+               element_info[trigger_element].group;
 
-         for (k = 0; k < group->num_elements_resolved; k++)
-           trigger_events[group->element_resolved[k]]
-             |= ei->change_page[j].events;
+             for (l = 0; l < group->num_elements_resolved; l++)
+               trigger_events[group->element_resolved[l]][k] = TRUE;
+           }
+           else
+             trigger_events[trigger_element][k] = TRUE;
+         }
        }
-       else
-         trigger_events[trigger_element] |= ei->change_page[j].events;
       }
     }
   }
@@ -1443,8 +1534,9 @@ static void InitGameEngine()
   /* add trigger events from element change event properties */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
-      trigger_events[element_info[i].change->trigger_element] |=
-       element_info[i].change->events;
+      for (j = 0; j < NUM_CHANGE_EVENTS; j++)
+       if (element_info[i].change->has_event[j])
+         trigger_events[element_info[i].change->trigger_element][j] = TRUE;
 #endif
 
   /* ---------- initialize push delay -------------------------------------- */
@@ -1610,7 +1702,7 @@ void InitGame()
     player->use_murphy_graphic = FALSE;
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
-    player->block_delay = -1;          /* initialized in InitPlayerField() */
+    player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
 
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
@@ -1775,8 +1867,8 @@ void InitGame()
       Stop[x][y] = FALSE;
       Pushed[x][y] = FALSE;
 
-      Changed[x][y] = CE_BITMASK_DEFAULT;
-      ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+      Changed[x][y] = FALSE;
+      ChangeEvent[x][y] = -1;
 
       ExplodePhase[x][y] = 0;
       ExplodeDelay[x][y] = 0;
@@ -1878,8 +1970,8 @@ void InitGame()
 #endif
 
 #if USE_NEW_BLOCK_STYLE
-         player->block_last_field = some_player->block_last_field;
-         player->block_delay = some_player->block_delay;
+         player->block_last_field       = some_player->block_last_field;
+         player->block_delay_adjustment = some_player->block_delay_adjustment;
 #endif
 
          StorePlayer[jx][jy] = player->element_nr;
@@ -3311,7 +3403,11 @@ void Explode(int ex, int ey, int phase, int mode)
        continue;
 #endif
 
-#if 1
+      /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
+        behaviour, for example when touching a yamyam that explodes to rocks
+        with active deadly shield, a rock is created under the player !!! */
+      /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
+#if 0
       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
          (game.engine_version < VERSION_IDENT(3,1,0,0) ||
           (x == ex && y == ey && mode != EX_TYPE_BORDER)))
@@ -5475,6 +5571,15 @@ void StartMoving(int x, int y)
     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
              CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
 
+#if USE_IMPACT_BUGFIX
+            (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
+             CAN_FALL(element) && WasJustFalling[x][y] &&
+             (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
+
+            (game.engine_version < VERSION_IDENT(2,2,0,7) &&
+             CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
+             (Feld[x][y + 1] == EL_BLOCKED)))
+#else
             (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
              CAN_SMASH(element) && WasJustFalling[x][y] &&
              (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
@@ -5482,6 +5587,7 @@ void StartMoving(int x, int y)
             (game.engine_version < VERSION_IDENT(2,2,0,7) &&
              CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
              (Feld[x][y + 1] == EL_BLOCKED)))
+#endif
 
 #else
 #if 1
@@ -6479,8 +6585,8 @@ void ContinueMoving(int x, int y)
 
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
-  Changed[x][y] = CE_BITMASK_DEFAULT;
-  ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+  Changed[x][y] = FALSE;
+  ChangeEvent[x][y] = -1;
 
   /* copy animation control values to new field */
   GfxFrame[newx][newy]  = GfxFrame[x][y];
@@ -6645,7 +6751,7 @@ void ContinueMoving(int x, int y)
   /* give the player one last chance (one more frame) to move away */
   if (CAN_FALL(element) && direction == MV_DOWN &&
       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
-      (!IS_PLAYER(x, newy + 1) ||
+      ((newy < lev_fieldy - 1 && !IS_PLAYER(x, newy + 1)) ||
        game.engine_version < VERSION_IDENT(3,1,1,0)))
     Impact(x, newy);
 #else
@@ -6658,7 +6764,7 @@ void ContinueMoving(int x, int y)
 
 #if USE_PUSH_BUGFIX
 #if 1
-  if (pushed_by_player && !game.use_bug_change_when_pushing)
+  if (pushed_by_player && !game.use_change_when_pushing_bug)
 #else
   if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
 #endif
@@ -6738,7 +6844,7 @@ void ContinueMoving(int x, int y)
              &element_info[hitting_element].change_page[i];
 
            if (change->can_change &&
-               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
+               change->has_event[CE_OTHER_IS_HITTING] &&
                change->trigger_side & touched_side &&
                change->trigger_element == touched_element)
            {
@@ -6758,7 +6864,7 @@ void ContinueMoving(int x, int y)
              &element_info[touched_element].change_page[i];
 
            if (change->can_change &&
-               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
+               change->has_event[CE_OTHER_GETS_HIT] &&
                change->trigger_side & hitting_side &&
                change->trigger_element == hitting_element)
            {
@@ -7793,6 +7899,8 @@ static void ChangeElementNowExt(int x, int y, int 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
 
@@ -7810,26 +7918,31 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
   int old_element = Feld[x][y];
 
   /* always use default change event to prevent running into a loop */
-  if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
-    ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
+  if (ChangeEvent[x][y] == -1)
+    ChangeEvent[x][y] = CE_DELAY;
 
-  if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
+  if (ChangeEvent[x][y] == CE_DELAY)
   {
     /* reset actual trigger element and player */
     change->actual_trigger_element = EL_EMPTY;
     change->actual_trigger_player = EL_PLAYER_1;
   }
 
-  /* do not change already changed elements with same change event */
-#if 0
-  if (Changed[x][y] & ChangeEvent[x][y])
+#if 1
+  /* do not change any elements that have already changed in this frame */
+  if (Changed[x][y])
     return FALSE;
 #else
-  if (Changed[x][y])
+  /* do not change already changed elements with same change event */
+  if (Changed[x][y] & ChangeEvent[x][y])
     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
 
 #if 0
   /* !!! indirect change before direct change !!! */
@@ -8100,7 +8213,7 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly,
   int i, j, x, y;
   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
 
-  if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+  if (!(trigger_events[trigger_element][trigger_event]))
     return FALSE;
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -8118,14 +8231,14 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly,
       struct ElementChangeInfo *change = &element_info[element].change_page[j];
 
       if (change->can_change &&
-         change->events & CH_EVENT_BIT(trigger_event) &&
+         change->has_event[trigger_event] &&
          change->trigger_side & trigger_side &&
          change->trigger_player & trigger_player &&
          change->trigger_page & trigger_page_bits &&
          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
       {
 #if 0
-       if (!(change->events & CH_EVENT_BIT(trigger_event)))
+       if (!(change->has_event[trigger_event]))
          printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
                 trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
 #endif
@@ -8153,7 +8266,7 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly,
       if (Feld[x][y] == element)
       {
        ChangeDelay[x][y] = 1;
-       ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
+       ChangeEvent[x][y] = trigger_event;
        ChangeElement(x, y, page);
       }
     }
@@ -8204,7 +8317,7 @@ static boolean CheckElementChangeExt(int x, int y,
       struct ElementChangeInfo *change = &element_info[element].change_page[i];
 
       if (change->can_change &&
-         change->events & CH_EVENT_BIT(trigger_event) &&
+         change->has_event[trigger_event] &&
          change->trigger_side & trigger_side &&
          change->trigger_player & trigger_player)
       {
@@ -8242,7 +8355,7 @@ static boolean CheckElementChangeExt(int x, int y,
 #endif
 
   ChangeDelay[x][y] = 1;
-  ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
+  ChangeEvent[x][y] = trigger_event;
   ChangeElement(x, y, trigger_page);
 
   return TRUE;
@@ -8870,8 +8983,8 @@ void GameActions()
 
   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
   {
-    Changed[x][y] = CE_BITMASK_DEFAULT;
-    ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
+    Changed[x][y] = FALSE;
+    ChangeEvent[x][y] = -1;
 
 #if USE_NEW_BLOCK_STYLE
     /* this must be handled before main playfield loop */
@@ -9485,7 +9598,9 @@ static boolean canFallDown(struct PlayerInfo *player)
   return (IN_LEV_FIELD(jx, jy + 1) &&
          (IS_FREE(jx, jy + 1) ||
 #if USE_NEW_BLOCK_STYLE
+#if USE_GRAVITY_BUGFIX_OLD
           Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
+#endif
 #endif
           (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
          IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
@@ -10151,11 +10266,48 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       printf("::: ALERT! block_delay == %d\n", player->block_delay);
 #endif
 
-    if (player->block_delay > 0 &&
+    if ((player->block_last_field || player->block_delay_adjustment > 0) &&
        Feld[last_jx][last_jy] == EL_EMPTY)
     {
+      int last_field_block_delay = 0;  /* start with no blocking at all */
+      int block_delay_adjustment = player->block_delay_adjustment;
+
+      /* if player blocks last field, add delay for exactly one move */
+      if (player->block_last_field)
+      {
+       last_field_block_delay += player->move_delay_value;
+
+#if USE_GRAVITY_BUGFIX_NEW
+       /* when blocking enabled, prevent moving up despite gravity */
+       if (game.gravity && player->MovDir == MV_UP)
+         block_delay_adjustment = -1;
+#endif
+      }
+
+      /* add block delay adjustment (also possible when not blocking) */
+      last_field_block_delay += block_delay_adjustment;
+
+#if 0
+#if USE_BLOCK_DELAY_BUGFIX
+      /* when blocking enabled, correct block delay for fast movement */
+      if (player->block_last_field &&
+         player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
+       last_field_block_delay =
+         player->move_delay_value + player->block_delay_adjustment;
+#endif
+#endif
+
+#if 0
+#if USE_GRAVITY_BUGFIX_NEW
+      /* when blocking enabled, correct block delay for gravity movement */
+      if (player->block_last_field &&
+         game.gravity && player->MovDir == MV_UP)
+       last_field_block_delay = player->move_delay_value - 1;
+#endif
+#endif
+
       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-      MovDelay[last_jx][last_jy] = player->block_delay + 1;
+      MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
     }
 #else
 #if USE_NEW_MOVE_STYLE
@@ -10535,7 +10687,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
          &element_info[center_element].change_page[j];
 
        if (change->can_change &&
-           change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+           change->has_event[CE_OTHER_IS_TOUCHING] &&
            change->trigger_side & border_side &&
 #if 1
            IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
@@ -10563,7 +10715,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
          &element_info[border_element].change_page[j];
 
        if (change->can_change &&
-           change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+           change->has_event[CE_OTHER_IS_TOUCHING] &&
            change->trigger_side & center_side &&
 #if 1
            IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
@@ -10656,7 +10808,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
            &element_info[hitting_element].change_page[i];
 
          if (change->can_change &&
-             change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
+             change->has_event[CE_OTHER_IS_HITTING] &&
              change->trigger_side & touched_side &&
          
 #if 1
@@ -10682,7 +10834,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
            &element_info[touched_element].change_page[i];
 
          if (change->can_change &&
-             change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
+             change->has_event[CE_OTHER_GETS_HIT] &&
              change->trigger_side & hitting_side &&
 #if 1
              IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
@@ -10763,7 +10915,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
            &element_info[hitting_element].change_page[i];
 
          if (change->can_change &&
-             change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
+             change->has_event[CE_OTHER_IS_SMASHING] &&
              change->trigger_side & touched_side &&
          
 #if 1
@@ -10789,7 +10941,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
            &element_info[touched_element].change_page[i];
 
          if (change->can_change &&
-             change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
+             change->has_event[CE_OTHER_GETS_SMASHED] &&
              change->trigger_side & hitting_side &&
 #if 1
              IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
@@ -11877,7 +12029,7 @@ int DigField(struct PlayerInfo *player,
 #if USE_PUSH_BUGFIX
        /* now: check for element change _after_ element has been pushed! */
 #if 1
-       if (game.use_bug_change_when_pushing)
+       if (game.use_change_when_pushing_bug)
 #else
        if (game.engine_version < VERSION_IDENT(3,1,0,0))
 #endif
@@ -11993,6 +12145,7 @@ int DigField(struct PlayerInfo *player,
          Feld[x][y] = EL_LAMP_ACTIVE;
          local_player->lights_still_needed--;
 
+         ResetGfxAnimation(x, y);
          DrawLevelField(x, y);
        }
        else if (element == EL_TIME_ORB_FULL)
@@ -12001,6 +12154,7 @@ int DigField(struct PlayerInfo *player,
          TimeLeft += 10;
          DrawGameValue_Time(TimeLeft);
 
+         ResetGfxAnimation(x, y);
          DrawLevelField(x, y);
 
 #if 0
@@ -12064,8 +12218,13 @@ int DigField(struct PlayerInfo *player,
   player->push_delay = 0;
 #endif
 
-  if (Feld[x][y] != element)           /* really digged/collected something */
-    player->is_collecting = !player->is_digging;
+#if USE_PENGUIN_COLLECT_BUGFIX
+  if (is_player)               /* function can also be called by EL_PENGUIN */
+#endif
+  {
+    if (Feld[x][y] != element)         /* really digged/collected something */
+      player->is_collecting = !player->is_digging;
+  }
 
   return MF_MOVING;
 }
@@ -12256,7 +12415,7 @@ boolean DropElement(struct PlayerInfo *player)
 
 #if 1
     /* needed if previous element just changed to "empty" in the last frame */
-    Changed[dropx][dropy] = 0;         /* allow another change */
+    Changed[dropx][dropy] = FALSE;             /* allow another change */
 #endif
 
 #if 1
@@ -12326,7 +12485,7 @@ boolean DropElement(struct PlayerInfo *player)
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
 
 #if 1
-    Changed[dropx][dropy] = 0;         /* allow another change */
+    Changed[dropx][dropy] = FALSE;             /* allow another change */
     CheckCollision[dropx][dropy] = 2;
 #else
 
@@ -12345,7 +12504,7 @@ boolean DropElement(struct PlayerInfo *player)
     /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
     else
     {
-      Changed[dropx][dropy] = 0;       /* allow another change */
+      Changed[dropx][dropy] = FALSE;   /* allow another change */
 
 #if 1
       TestIfElementHitsCustomElement(dropx, dropy, move_direction);