rnd-20031109-2-src
[rocksndiamonds.git] / src / game.c
index 87b47a8c157ef92377c5f049ad90f0931afc6774..71013bf598ebd1a491c8cc3a52f2311f0db2fdbe 100644 (file)
@@ -98,6 +98,8 @@
                                 RND(element_info[e].push_delay_random))
 #define GET_NEW_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
                                 RND(element_info[e].move_delay_random))
+#define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
+                                   (element_info[e].move_delay_random))
 
 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)            \
                (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
@@ -677,7 +679,7 @@ static void InitField(int x, int y, boolean init_game)
 
     case EL_PIG:
     case EL_DRAGON:
-      MovDir[x][y] = 1 << RND(4);
+      GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
       break;
 
 #if 0
@@ -800,13 +802,19 @@ static void InitGameEngine()
 
   /* dynamically adjust player properties according to game engine version */
   game.initial_move_delay =
-    (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
+    (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
      INITIAL_MOVE_DELAY_OFF);
 
   /* dynamically adjust player properties according to level information */
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
+  /* ---------- initialize player's initial push delay --------------------- */
+
+  /* dynamically adjust player properties according to game engine version */
+  game.initial_push_delay_value =
+    (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
+
   /* ---------- initialize changing elements ------------------------------- */
 
   /* initialize changing elements information */
@@ -927,9 +935,8 @@ static void InitGameEngine()
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
-      element_info[i].push_delay_fixed =
-       (game.engine_version < VERSION_IDENT(3,0,7) ? 2 : 8);
-      element_info[i].push_delay_random = 8;
+      element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
+      element_info[i].push_delay_random = game.default_push_delay_random;
     }
   }
 
@@ -1060,7 +1067,7 @@ void InitGame()
     player->move_delay_value = game.initial_move_delay_value;
 
     player->push_delay = 0;
-    player->push_delay_value = 5;
+    player->push_delay_value = game.initial_push_delay_value;
 
     player->last_jx = player->last_jy = 0;
     player->jx = player->jy = 0;
@@ -1143,9 +1150,10 @@ void InitGame()
       ExplodeField[x][y] = EX_NO_EXPLOSION;
 
       GfxFrame[x][y] = 0;
-      GfxAction[x][y] = ACTION_DEFAULT;
       GfxRandom[x][y] = INIT_GFX_RANDOM();
       GfxElement[x][y] = EL_UNDEFINED;
+      GfxAction[x][y] = ACTION_DEFAULT;
+      GfxDir[x][y] = MV_NO_MOVING;
     }
   }
 
@@ -1597,6 +1605,8 @@ void InitMovDir(int x, int y)
       }
       break;
   }
+
+  GfxDir[x][y] = MovDir[x][y];
 }
 
 void InitAmoebaNr(int x, int y)
@@ -1841,6 +1851,7 @@ static void ResetGfxAnimation(int x, int y)
 {
   GfxFrame[x][y] = 0;
   GfxAction[x][y] = ACTION_DEFAULT;
+  GfxDir[x][y] = MovDir[x][y];
 }
 
 void InitMovingField(int x, int y, int direction)
@@ -1855,6 +1866,7 @@ void InitMovingField(int x, int y, int direction)
     ResetGfxAnimation(x, y);
 
   MovDir[newx][newy] = MovDir[x][y] = direction;
+  GfxDir[x][y] = direction;
 
   if (Feld[newx][newy] == EL_EMPTY)
     Feld[newx][newy] = EL_BLOCKED;
@@ -1865,8 +1877,9 @@ void InitMovingField(int x, int y, int direction)
     GfxAction[x][y] = ACTION_MOVING;
 
   GfxFrame[newx][newy] = GfxFrame[x][y];
-  GfxAction[newx][newy] = GfxAction[x][y];
   GfxRandom[newx][newy] = GfxRandom[x][y];
+  GfxAction[newx][newy] = GfxAction[x][y];
+  GfxDir[newx][newy] = GfxDir[x][y];
 }
 
 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
@@ -1950,6 +1963,7 @@ static void RemoveField(int x, int y)
 
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
+  GfxDir[x][y] = MV_NO_MOVING;
 }
 
 void RemoveMovingField(int x, int y)
@@ -2150,7 +2164,7 @@ void Explode(int ex, int ey, int phase, int mode)
 #if 0
     /* --- This is only really needed (and now handled) in "Impact()". --- */
     /* do not explode moving elements that left the explode field in time */
-    if (game.engine_version >= RELEASE_IDENT(2,2,0,7) &&
+    if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
        center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
       return;
 #endif
@@ -2205,7 +2219,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
 #else
       if ((IS_INDESTRUCTIBLE(element) &&
-          (game.engine_version < VERSION_IDENT(2,2,0) ||
+          (game.engine_version < VERSION_IDENT(2,2,0,0) ||
            (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
          element == EL_FLAMES)
        continue;
@@ -2327,6 +2341,7 @@ void Explode(int ex, int ey, int phase, int mode)
       RemoveField(x, y);
 #else
       MovDir[x][y] = MovPos[x][y] = 0;
+      GfxDir[x][y] = MovDir[x][y];
       AmoebaNr[x][y] = 0;
 #endif
 #endif
@@ -2415,6 +2430,7 @@ void Explode(int ex, int ey, int phase, int mode)
     Back[x][y] = 0;
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+    GfxDir[x][y] = MV_NO_MOVING;
     ChangeDelay[x][y] = 0;
     ChangePage[x][y] = -1;
 
@@ -2966,7 +2982,7 @@ void Impact(int x, int y)
                                         MovPos[x][y + 1] <= TILEY / 2));
 
     /* do not smash moving elements that left the smashed field in time */
-    if (game.engine_version >= RELEASE_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
+    if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
        ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
       object_hit = FALSE;
 
@@ -3173,7 +3189,7 @@ void Impact(int x, int y)
     PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
 }
 
-void TurnRound(int x, int y)
+inline static void TurnRoundExt(int x, int y)
 {
   static struct
   {
@@ -3713,6 +3729,32 @@ void TurnRound(int x, int y)
   }
 }
 
+static void TurnRound(int x, int y)
+{
+  int direction = MovDir[x][y];
+
+#if 0
+  GfxDir[x][y] = MovDir[x][y];
+#endif
+
+  TurnRoundExt(x, y);
+
+#if 1
+  GfxDir[x][y] = MovDir[x][y];
+#endif
+
+  if (direction != MovDir[x][y])
+    GfxFrame[x][y] = 0;
+
+#if 1
+  if (MovDelay[x][y])
+    GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
+#else
+  if (MovDelay[x][y])
+    GfxAction[x][y] = ACTION_WAITING;
+#endif
+}
+
 static boolean JustBeingPushed(int x, int y)
 {
   int i;
@@ -3736,16 +3778,21 @@ static boolean JustBeingPushed(int x, int y)
 
 void StartMoving(int x, int y)
 {
-  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
+  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
   boolean started_moving = FALSE;      /* some elements can fall _and_ move */
   int element = Feld[x][y];
 
   if (Stop[x][y])
     return;
 
+#if 1
+  if (MovDelay[x][y] == 0)
+    GfxAction[x][y] = ACTION_DEFAULT;
+#else
   /* !!! this should be handled more generic (not only for mole) !!! */
   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
     GfxAction[x][y] = ACTION_DEFAULT;
+#endif
 
   if (CAN_FALL(element) && y < lev_fieldy - 1)
   {
@@ -3892,16 +3939,16 @@ void StartMoving(int x, int y)
 #endif
     }
 #if 1
-    else if ((game.engine_version < RELEASE_IDENT(2,2,0,7) &&
+    else if ((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)) ||
-            (game.engine_version >= VERSION_IDENT(3,0,7) &&
+            (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))))
 
 #else
 #if 1
-    else if (game.engine_version < RELEASE_IDENT(2,2,0,7) &&
+    else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
             CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
             WasJustMoving[x][y] && !Pushed[x][y + 1])
 #else
@@ -4089,7 +4136,13 @@ void StartMoving(int x, int y)
        /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
        ResetGfxAnimation(x, y);
 #endif
+
+#if 0
+       if (GfxAction[x][y] != ACTION_WAITING)
+         printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
+
        GfxAction[x][y] = ACTION_WAITING;
+#endif
       }
 
       if (element == EL_ROBOT ||
@@ -4176,10 +4229,12 @@ void StartMoving(int x, int y)
        return;
       }
 
+#if 0
       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
         for all other elements GfxAction will be set by InitMovingField() */
       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
        GfxAction[x][y] = ACTION_MOVING;
+#endif
     }
 
     /* now make next step */
@@ -4235,7 +4290,7 @@ void StartMoving(int x, int y)
        if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
          DrawLevelField(newx, newy);
        else
-         MovDir[x][y] = MV_NO_MOVING;
+         GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
       }
       else if (!IS_FREE(newx, newy))
       {
@@ -4547,8 +4602,9 @@ void ContinueMoving(int x, int y)
 
   /* copy animation control values to new field */
   GfxFrame[newx][newy]  = GfxFrame[x][y];
-  GfxAction[newx][newy] = GfxAction[x][y];     /* keep action one frame */
   GfxRandom[newx][newy] = GfxRandom[x][y];     /* keep same random value */
+  GfxAction[newx][newy] = GfxAction[x][y];     /* keep action one frame  */
+  GfxDir[newx][newy]    = GfxDir[x][y];                /* keep element direction */
 
   Pushed[x][y] = Pushed[newx][newy] = FALSE;
 
@@ -4573,7 +4629,7 @@ void ContinueMoving(int x, int y)
 
   if (!CAN_MOVE(element) ||
       (CAN_FALL(element) && direction == MV_DOWN))
-    MovDir[newx][newy] = 0;
+    GfxDir[x][y] = MovDir[newx][newy] = 0;
 
 #endif
 #endif
@@ -4601,7 +4657,9 @@ void ContinueMoving(int x, int y)
   {
     TestIfBadThingTouchesHero(newx, newy);
     TestIfBadThingTouchesFriend(newx, newy);
-    TestIfBadThingTouchesOtherBadThing(newx, newy);
+
+    if (!IS_CUSTOM_ELEMENT(element))
+      TestIfBadThingTouchesOtherBadThing(newx, newy);
   }
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
@@ -5266,7 +5324,7 @@ void MauerWaechst(int x, int y)
 
     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
     {
-      int graphic = el_dir2img(Feld[x][y], MovDir[x][y]);
+      int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
 
       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
@@ -5297,7 +5355,7 @@ void MauerWaechst(int x, int y)
 
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      MovDir[x][y] = MV_NO_MOVING;
+      GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
       DrawLevelField(x, y);
     }
   }
@@ -5342,7 +5400,7 @@ void MauerAbleger(int ax, int ay)
     {
       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax][ay-1] = element;
-      MovDir[ax][ay-1] = MV_UP;
+      GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
        DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
                    IMG_EXPANDABLE_WALL_GROWING_UP, 0);
@@ -5352,7 +5410,7 @@ void MauerAbleger(int ax, int ay)
     {
       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax][ay+1] = element;
-      MovDir[ax][ay+1] = MV_DOWN;
+      GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
        DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
                    IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
@@ -5368,7 +5426,7 @@ void MauerAbleger(int ax, int ay)
     {
       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax-1][ay] = element;
-      MovDir[ax-1][ay] = MV_LEFT;
+      GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
        DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
                    IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
@@ -5379,7 +5437,7 @@ void MauerAbleger(int ax, int ay)
     {
       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
       Store[ax+1][ay] = element;
-      MovDir[ax+1][ay] = MV_RIGHT;
+      GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
        DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
                    IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
@@ -5713,7 +5771,7 @@ static void ChangeElement(int x, int y, int page)
 
   if (ChangeDelay[x][y] != 0)          /* continue element change */
   {
-    int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+    int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 
     if (IS_ANIMATED(graphic))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
@@ -5770,9 +5828,18 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly,
       struct ElementChangeInfo *change = &element_info[element].change_page[j];
 
       if (change->can_change &&
+#if 1
+         change->events & CH_EVENT_BIT(trigger_event) &&
+#endif
          change->sides & trigger_side &&
          change->trigger_element == trigger_element)
       {
+#if 0
+       if (!(change->events & CH_EVENT_BIT(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
+
        change_element = TRUE;
        page = j;
 
@@ -5839,6 +5906,100 @@ static boolean CheckElementChange(int x, int y, int element, int trigger_event)
   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
 }
 
+#if 1
+static byte PlayerActions(struct PlayerInfo *player, byte player_action)
+{
+#if 0
+  static byte stored_player_action[MAX_PLAYERS];
+  static int num_stored_actions = 0;
+#endif
+  boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
+  int left     = player_action & JOY_LEFT;
+  int right    = player_action & JOY_RIGHT;
+  int up       = player_action & JOY_UP;
+  int down     = player_action & JOY_DOWN;
+  int button1  = player_action & JOY_BUTTON_1;
+  int button2  = player_action & JOY_BUTTON_2;
+  int dx       = (left ? -1    : right ? 1     : 0);
+  int dy       = (up   ? -1    : down  ? 1     : 0);
+
+#if 0
+  stored_player_action[player->index_nr] = 0;
+  num_stored_actions++;
+#endif
+
+#if 0
+  printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
+#endif
+
+  if (!player->active || tape.pausing)
+    return 0;
+
+  if (player_action)
+  {
+#if 0
+    printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
+#endif
+
+    if (button1)
+      snapped = SnapField(player, dx, dy);
+    else
+    {
+      if (button2)
+       dropped = DropElement(player);
+
+      moved = MovePlayer(player, dx, dy);
+    }
+
+    if (tape.single_step && tape.recording && !tape.pausing)
+    {
+      if (button1 || (dropped && !moved))
+      {
+       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+       SnapField(player, 0, 0);                /* stop snapping */
+      }
+    }
+
+#if 1
+    return player_action;
+#else
+    stored_player_action[player->index_nr] = player_action;
+#endif
+  }
+  else
+  {
+#if 0
+    printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
+#endif
+
+    /* no actions for this player (no input at player's configured device) */
+
+    DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
+    SnapField(player, 0, 0);
+    CheckGravityMovement(player);
+
+    if (player->MovPos == 0)
+      InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
+
+    if (player->MovPos == 0)   /* needed for tape.playing */
+      player->is_moving = FALSE;
+
+    return 0;
+  }
+
+#if 0
+  if (tape.recording && num_stored_actions >= MAX_PLAYERS)
+  {
+    printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
+
+    TapeRecordAction(stored_player_action);
+    num_stored_actions = 0;
+  }
+#endif
+}
+
+#else
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -5856,11 +6017,15 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
   stored_player_action[player->index_nr] = 0;
   num_stored_actions++;
 
+  printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
+
   if (!player->active || tape.pausing)
     return;
 
   if (player_action)
   {
+    printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
+
     if (button1)
       snapped = SnapField(player, dx, dy);
     else
@@ -5884,6 +6049,8 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
   }
   else
   {
+    printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
+
     /* no actions for this player (no input at player's configured device) */
 
     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
@@ -5899,10 +6066,13 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 
   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
   {
+    printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
+
     TapeRecordAction(stored_player_action);
     num_stored_actions = 0;
   }
 }
+#endif
 
 void GameActions()
 {
@@ -5912,6 +6082,9 @@ void GameActions()
   int i, x, y, element, graphic;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
+#if 1
+  byte tape_action[MAX_PLAYERS];
+#endif
 
   if (game_status != GAME_MODE_PLAYING)
     return;
@@ -5974,7 +6147,7 @@ void GameActions()
   if (!options.network && !setup.team_mode)
     local_player->effective_action = summarized_player_action;
 
-  for (i=0; i<MAX_PLAYERS; i++)
+  for (i=0; i < MAX_PLAYERS; i++)
   {
     int actual_player_action = stored_player[i].effective_action;
 
@@ -5984,10 +6157,16 @@ void GameActions()
     if (recorded_player_action)
       actual_player_action = recorded_player_action[i];
 
-    PlayerActions(&stored_player[i], actual_player_action);
+    tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
+
     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
   }
 
+#if 1
+  if (tape.recording)
+    TapeRecordAction(tape_action);
+#endif
+
   network_player_action_received = FALSE;
 
   ScrollScreen(NULL, SCROLL_GO_ON);
@@ -6001,7 +6180,7 @@ void GameActions()
 #endif
 
 #if 1
-  if (game.engine_version < RELEASE_IDENT(2,2,0,7))
+  if (game.engine_version < VERSION_IDENT(2,2,0,7))
   {
     for (i=0; i<MAX_PLAYERS; i++)
     {
@@ -6078,7 +6257,7 @@ void GameActions()
   {
     element = Feld[x][y];
 #if 1
-    graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+    graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 #else
     graphic = el2img(element);
 #endif
@@ -6115,7 +6294,12 @@ void GameActions()
 
 #if 1
     /* this may take place after moving, so 'element' may have changed */
+#if 0
     if (IS_CHANGING(x, y))
+#else
+    if (IS_CHANGING(x, y) &&
+       (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
+#endif
     {
 #if 0
       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
@@ -6125,7 +6309,7 @@ void GameActions()
 #endif
 
       element = Feld[x][y];
-      graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+      graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
     }
 #endif
 
@@ -6134,7 +6318,7 @@ void GameActions()
       StartMoving(x, y);
 
 #if 1
-      graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+      graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 #if 0
       if (element == EL_MOLE)
        printf("::: %d, %d, %d [%d]\n",
@@ -6154,6 +6338,11 @@ void GameActions()
       {
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#if 0
+       if (element == EL_BUG)
+         printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
+#endif
+
 #if 0
        if (element == EL_MOLE)
          printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
@@ -6887,7 +7076,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->is_moving = FALSE;
   }
 
-  if (game.engine_version < VERSION_IDENT(3,0,7))
+  if (game.engine_version < VERSION_IDENT(3,0,7,0))
   {
     TestIfHeroTouchesBadThing(jx, jy);
     TestIfPlayerTouchesCustomElement(jx, jy);
@@ -6964,7 +7153,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        player->LevelSolved = player->GameOver = TRUE;
     }
 
-    if (game.engine_version >= VERSION_IDENT(3,0,7))
+    if (game.engine_version >= VERSION_IDENT(3,0,7,0))
     {
       TestIfHeroTouchesBadThing(jx, jy);
       TestIfPlayerTouchesCustomElement(jx, jy);
@@ -7050,7 +7239,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
 
     if (IS_PLAYER(x, y))
     {
-      if (game.engine_version < VERSION_IDENT(3,0,7))
+      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];
@@ -7066,7 +7255,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     }
     else if (IS_PLAYER(xx, yy))
     {
-      if (game.engine_version >= VERSION_IDENT(3,0,7))
+      if (game.engine_version >= VERSION_IDENT(3,0,7,0))
       {
        struct PlayerInfo *player = PLAYERINFO(xx, yy);
 
@@ -7124,7 +7313,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    if (game.engine_version < VERSION_IDENT(3,0,7))
+    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];
@@ -7304,7 +7493,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
        if (bad_element == EL_ROBOT && player->is_moving)
          continue;     /* robot does not kill player if he is moving */
 
-       if (game.engine_version >= VERSION_IDENT(3,0,7))
+       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
        {
          if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
            continue;           /* center and border element do not touch */
@@ -7514,7 +7703,7 @@ int DigField(struct PlayerInfo *player,
     CH_SIDE_BOTTOM,    /* moving up    */
     CH_SIDE_TOP,       /* moving down  */
   };
-  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
+  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
   int jx = player->jx, jy = player->jy;
   int dx = x - jx, dy = y - jy;
   int nextx = x + dx, nexty = y + dy;
@@ -7549,7 +7738,7 @@ int DigField(struct PlayerInfo *player,
   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
 #else
   if (IS_TUBE(Feld[jx][jy]) ||
-      (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0)))
+      (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
 #endif
   {
     int i = 0;
@@ -7584,7 +7773,7 @@ int DigField(struct PlayerInfo *player,
   element = Feld[x][y];
 
   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
-      game.engine_version >= VERSION_IDENT(2,2,0))
+      game.engine_version >= VERSION_IDENT(2,2,0,0))
     return MF_NO_ACTION;
 
   switch (element)
@@ -7880,6 +8069,21 @@ int DigField(struct PlayerInfo *player,
            !(element == EL_SPRING && use_spring_bug))
          return MF_NO_ACTION;
 
+#if 1
+       if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
+           ((move_direction & MV_VERTICAL &&
+             ((element_info[element].move_pattern & MV_LEFT &&
+               IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
+              (element_info[element].move_pattern & MV_RIGHT &&
+               IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
+            (move_direction & MV_HORIZONTAL &&
+             ((element_info[element].move_pattern & MV_UP &&
+               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;
+#endif
+
 #if 1
        /* do not push elements already moving away faster than player */
        if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
@@ -7889,9 +8093,36 @@ int DigField(struct PlayerInfo *player,
        if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
          return MF_NO_ACTION;
 #endif
+
+#if 1
+       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
+       {
+         if (player->push_delay_value == -1)
+           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+       }
+       else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
+       {
+         if (!player->is_pushing)
+           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+       }
+
+       /*
+       if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
+           (game.engine_version < VERSION_IDENT(3,0,7,1) ||
+            !player_is_pushing))
+         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+       */
+#else
        if (!player->is_pushing &&
-           game.engine_version >= RELEASE_IDENT(2,2,0,7))
+           game.engine_version >= VERSION_IDENT(2,2,0,7))
          player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+#endif
+
+#if 0
+       printf("::: push delay: %ld [%d, %d] [%d]\n",
+              player->push_delay_value, FrameCounter, game.engine_version,
+              player->is_pushing);
+#endif
 
        player->is_pushing = TRUE;
 
@@ -7912,12 +8143,16 @@ int DigField(struct PlayerInfo *player,
            element != EL_SPRING && element != EL_BALLOON)
        {
          /* make sure that there is no move delay before next try to push */
-         if (game.engine_version >= VERSION_IDENT(3,0,7))
+         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
            player->move_delay = INITIAL_MOVE_DELAY_OFF;
 
          return MF_NO_ACTION;
        }
 
+#if 0
+       printf("::: NOW PUSHING... [%d]\n", FrameCounter);
+#endif
+
        if (IS_SB_ELEMENT(element))
        {
          if (element == EL_SOKOBAN_FIELD_FULL)
@@ -7964,8 +8199,10 @@ int DigField(struct PlayerInfo *player,
        Pushed[x][y] = TRUE;
        Pushed[nextx][nexty] = TRUE;
 
-       if (game.engine_version < RELEASE_IDENT(2,2,0,7))
+       if (game.engine_version < VERSION_IDENT(2,2,0,7))
          player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+       else
+         player->push_delay_value = -1;        /* get new value later */
 
        CheckTriggeredElementSideChange(x, y, element, dig_side,
                                        CE_OTHER_GETS_PUSHED);
@@ -8105,7 +8342,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                        dy == -1 ? MV_UP :
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
 
-  if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
+  if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
     return FALSE;
 
   if (!player->active || !IN_LEV_FIELD(x, y))