rnd-20031109-2-src
[rocksndiamonds.git] / src / game.c
index a46368e1a87874e31529b510d780db0d2b0848a9..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
@@ -1148,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;
     }
   }
 
@@ -1602,6 +1605,8 @@ void InitMovDir(int x, int y)
       }
       break;
   }
+
+  GfxDir[x][y] = MovDir[x][y];
 }
 
 void InitAmoebaNr(int x, int y)
@@ -1846,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)
@@ -1860,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;
@@ -1870,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)
@@ -1955,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)
@@ -2332,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
@@ -2420,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;
 
@@ -3178,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
   {
@@ -3718,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;
@@ -3748,9 +3785,14 @@ void StartMoving(int x, int 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)
   {
@@ -4094,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 ||
@@ -4181,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 */
@@ -4240,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))
       {
@@ -4552,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;
 
@@ -4578,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
@@ -4606,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);
@@ -5271,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);
@@ -5302,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);
     }
   }
@@ -5347,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);
@@ -5357,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);
@@ -5373,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);
@@ -5384,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);
@@ -5718,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);
@@ -5853,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];
@@ -5870,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
@@ -5898,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);
@@ -5913,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()
 {
@@ -5926,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;
@@ -5988,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;
 
@@ -5998,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);
@@ -6092,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
@@ -6129,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] :
@@ -6139,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
 
@@ -6148,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",
@@ -6168,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]);
@@ -7894,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 &&