rnd-20060314-1-src
[rocksndiamonds.git] / src / game.c
index 8d919bc8fb7a44aaa2c712a9d21f79e2a1459f09..f23555b3e7c5c00a96646b29037896f82ba986ba 100644 (file)
@@ -333,6 +333,32 @@ static void PlayLevelMusic();
 static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
+int AmoebeNachbarNr(int, int);
+void AmoebeUmwandeln(int, int);
+void ContinueMoving(int, int);
+void Bang(int, int);
+void InitMovDir(int, int);
+void InitAmoebaNr(int, int);
+int NewHiScore(void);
+
+void TestIfGoodThingHitsBadThing(int, int, int);
+void TestIfBadThingHitsGoodThing(int, int, int);
+void TestIfPlayerTouchesBadThing(int, int);
+void TestIfPlayerRunsIntoBadThing(int, int, int);
+void TestIfBadThingTouchesPlayer(int, int);
+void TestIfBadThingRunsIntoPlayer(int, int, int);
+void TestIfFriendTouchesBadThing(int, int);
+void TestIfBadThingTouchesFriend(int, int);
+void TestIfBadThingTouchesOtherBadThing(int, int);
+
+void KillPlayer(struct PlayerInfo *);
+void BuryPlayer(struct PlayerInfo *);
+void RemovePlayer(struct PlayerInfo *);
+
+boolean SnapField(struct PlayerInfo *, int, int);
+boolean DropElement(struct PlayerInfo *);
+
+
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
 
@@ -690,6 +716,18 @@ static int playfield_scan_delta_y = 1;
                                     (x) >= 0 && (x) <= lev_fieldx - 1; \
                                     (x) += playfield_scan_delta_x)     \
 
+#ifdef DEBUG
+void DEBUG_SetMaximumDynamite()
+{
+  int i;
+
+  for (i = 0; i < MAX_INVENTORY_SIZE; i++)
+    if (local_player->inventory_size < MAX_INVENTORY_SIZE)
+      local_player->inventory_element[local_player->inventory_size++] =
+       EL_DYNAMITE;
+}
+#endif
+
 static void InitPlayfieldScanModeVars()
 {
   if (game.use_reverse_scan_direction)
@@ -936,41 +974,45 @@ static void InitField(int x, int y, boolean init_game)
        Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
       break;
 
+    case EL_BUG:
     case EL_BUG_RIGHT:
     case EL_BUG_UP:
     case EL_BUG_LEFT:
     case EL_BUG_DOWN:
-    case EL_BUG:
+    case EL_SPACESHIP:
     case EL_SPACESHIP_RIGHT:
     case EL_SPACESHIP_UP:
     case EL_SPACESHIP_LEFT:
     case EL_SPACESHIP_DOWN:
-    case EL_SPACESHIP:
+    case EL_BD_BUTTERFLY:
     case EL_BD_BUTTERFLY_RIGHT:
     case EL_BD_BUTTERFLY_UP:
     case EL_BD_BUTTERFLY_LEFT:
     case EL_BD_BUTTERFLY_DOWN:
-    case EL_BD_BUTTERFLY:
+    case EL_BD_FIREFLY:
     case EL_BD_FIREFLY_RIGHT:
     case EL_BD_FIREFLY_UP:
     case EL_BD_FIREFLY_LEFT:
     case EL_BD_FIREFLY_DOWN:
-    case EL_BD_FIREFLY:
     case EL_PACMAN_RIGHT:
     case EL_PACMAN_UP:
     case EL_PACMAN_LEFT:
     case EL_PACMAN_DOWN:
     case EL_YAMYAM:
+    case EL_YAMYAM_LEFT:
+    case EL_YAMYAM_RIGHT:
+    case EL_YAMYAM_UP:
+    case EL_YAMYAM_DOWN:
     case EL_DARK_YAMYAM:
     case EL_ROBOT:
     case EL_PACMAN:
     case EL_SP_SNIKSNAK:
     case EL_SP_ELECTRON:
+    case EL_MOLE:
     case EL_MOLE_LEFT:
     case EL_MOLE_RIGHT:
     case EL_MOLE_UP:
     case EL_MOLE_DOWN:
-    case EL_MOLE:
       InitMovDir(x, y);
       break;
 
@@ -1749,6 +1791,10 @@ int get_num_special_action(int element, int action_first, int action_last)
       break;
   }
 
+#if 0
+  printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
+#endif
+
   return num_special_action;
 }
 
@@ -1845,11 +1891,15 @@ void InitGame()
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
+    player->dir_waiting = MV_NONE;
     player->action_waiting = ACTION_DEFAULT;
     player->last_action_waiting = ACTION_DEFAULT;
     player->special_action_bored = ACTION_DEFAULT;
     player->special_action_sleeping = ACTION_DEFAULT;
 
+#if 1
+    /* cannot be set here -- could be modified in Init[Player]Field() below */
+#else
     /* set number of special actions for bored and sleeping animation */
     player->num_special_action_bored =
       get_num_special_action(player->artwork_element,
@@ -1857,6 +1907,7 @@ void InitGame()
     player->num_special_action_sleeping =
       get_num_special_action(player->artwork_element,
                             ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+#endif
 
     player->switch_x = -1;
     player->switch_y = -1;
@@ -1945,6 +1996,7 @@ void InitGame()
   game.envelope_active = FALSE;
 
   game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
+  game.set_centered_player = FALSE;
 
   for (i = 0; i < NUM_BELTS; i++)
   {
@@ -2011,6 +2063,22 @@ void InitGame()
 
   InitBeltMovement();
 
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    struct PlayerInfo *player = &stored_player[i];
+
+#if 1
+    /* set number of special actions for bored and sleeping animation */
+    player->num_special_action_bored =
+      get_num_special_action(player->artwork_element,
+                            ACTION_BORING_1, ACTION_BORING_LAST);
+    player->num_special_action_sleeping =
+      get_num_special_action(player->artwork_element,
+                            ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+#endif
+
+  }
+
   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
@@ -2442,6 +2510,14 @@ void InitMovDir(int x, int y)
       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
       break;
 
+    case EL_YAMYAM_LEFT:
+    case EL_YAMYAM_RIGHT:
+    case EL_YAMYAM_UP:
+    case EL_YAMYAM_DOWN:
+      Feld[x][y] = EL_YAMYAM;
+      MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
+      break;
+
     case EL_SP_SNIKSNAK:
       MovDir[x][y] = MV_UP;
       break;
@@ -3108,6 +3184,183 @@ void CheckDynamite(int x, int y)
   Bang(x, y);
 }
 
+#if 1
+
+static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
+{
+  boolean num_checked_players = 0;
+  int i;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].active)
+    {
+      int sx = stored_player[i].jx;
+      int sy = stored_player[i].jy;
+
+      if (num_checked_players == 0)
+      {
+       *sx1 = *sx2 = sx;
+       *sy1 = *sy2 = sy;
+      }
+      else
+      {
+       *sx1 = MIN(*sx1, sx);
+       *sy1 = MIN(*sy1, sy);
+       *sx2 = MAX(*sx2, sx);
+       *sy2 = MAX(*sy2, sy);
+      }
+
+      num_checked_players++;
+    }
+  }
+}
+
+static boolean checkIfAllPlayersFitToScreen_RND()
+{
+  int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+
+  setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+  return (sx2 - sx1 < SCR_FIELDX &&
+         sy2 - sy1 < SCR_FIELDY);
+}
+
+static void setScreenCenteredToAllPlayers(int *sx, int *sy)
+{
+  int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
+
+  setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+  *sx = (sx1 + sx2) / 2;
+  *sy = (sy1 + sy2) / 2;
+}
+
+#if 0
+static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
+                                             int center_x, int center_y)
+{
+  int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
+
+  setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
+
+  *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
+  *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
+}
+
+static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
+{
+  int max_dx, max_dy;
+
+  setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
+
+  return (max_dx <= SCR_FIELDX / 2 &&
+         max_dy <= SCR_FIELDY / 2);
+}
+#endif
+
+#endif
+
+#if 1
+
+void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
+                       boolean quick_relocation)
+{
+  boolean ffwd_delay = (tape.playing && tape.fast_forward);
+  boolean no_delay = (tape.warp_forward);
+  int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
+  int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+
+  if (quick_relocation)
+  {
+    int offset = (setup.scroll_delay ? 3 : 0);
+
+#if 0
+    if (center_screen)
+      offset = 0;
+#endif
+
+    if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
+    {
+      scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                 x > SBX_Right + MIDPOSX ? SBX_Right :
+                 x - MIDPOSX);
+
+      scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                 y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                 y - MIDPOSY);
+    }
+    else
+    {
+      if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
+         (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
+       scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
+
+      if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
+         (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
+       scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
+
+      /* don't scroll over playfield boundaries */
+      if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+       scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+
+      /* don't scroll over playfield boundaries */
+      if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+       scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+    }
+
+    RedrawPlayfield(TRUE, 0,0,0,0);
+  }
+  else
+  {
+    int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
+                    x > SBX_Right + MIDPOSX ? SBX_Right :
+                    x - MIDPOSX);
+
+    int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
+                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
+                    y - MIDPOSY);
+
+    ScrollScreen(NULL, SCROLL_GO_ON);  /* scroll last frame to full tile */
+
+    while (scroll_x != scroll_xx || scroll_y != scroll_yy)
+    {
+      int dx = 0, dy = 0;
+      int fx = FX, fy = FY;
+
+      dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
+      dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
+
+      if (dx == 0 && dy == 0)          /* no scrolling needed at all */
+       break;
+
+      scroll_x -= dx;
+      scroll_y -= dy;
+
+      fx += dx * TILEX / 2;
+      fy += dy * TILEY / 2;
+
+      ScrollLevel(dx, dy);
+      DrawAllPlayers();
+
+      /* scroll in two steps of half tile size to make things smoother */
+      BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+      FlushDisplay();
+      Delay(wait_delay_value);
+
+      /* scroll second step to align at full tile size */
+      BackToFront();
+      Delay(wait_delay_value);
+    }
+
+    DrawAllPlayers();
+    BackToFront();
+    Delay(wait_delay_value);
+  }
+}
+
+#else
+
 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
 {
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
@@ -3200,6 +3453,8 @@ void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
   }
 }
 
+#endif
+
 void RelocatePlayer(int jx, int jy, int el_player_raw)
 {
   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
@@ -3277,8 +3532,13 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
 
 #if 1
   /* only visually relocate centered player */
+#if 1
+  DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
+                    level.instant_relocation);
+#else
   if (player->index_nr == game.centered_player_nr)
     DrawRelocatePlayer(player, level.instant_relocation);
+#endif
 #else
   if (player == local_player)  /* only visually relocate local player */
     DrawRelocatePlayer(player, level.instant_relocation);
@@ -8579,6 +8839,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   boolean last_waiting = player->is_waiting;
   int move_dir = player->MovDir;
 
+  player->dir_waiting = move_dir;
   player->last_action_waiting = player->action_waiting;
 
   if (is_waiting)
@@ -8596,7 +8857,11 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
        game.player_sleeping_delay_fixed +
        SimpleRND(game.player_sleeping_delay_random);
 
+#if 1
+      InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
+#else
       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
+#endif
     }
 
     if (game.player_sleeping_delay_fixed +
@@ -8614,6 +8879,26 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
                              player->is_bored ? ACTION_BORING :
                              ACTION_WAITING);
 
+#if 1
+    if (player->is_sleeping && player->use_murphy)
+    {
+      /* special case for sleeping Murphy when leaning against non-free tile */
+
+      if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
+         (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
+          !IS_MOVING(player->jx - 1, player->jy)))
+       move_dir = MV_LEFT;
+      else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
+              (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
+               !IS_MOVING(player->jx + 1, player->jy)))
+       move_dir = MV_RIGHT;
+      else
+       player->is_sleeping = FALSE;
+
+      player->dir_waiting = move_dir;
+    }
+#endif
+
     if (player->is_sleeping)
     {
       if (player->num_special_action_sleeping > 0)
@@ -8696,6 +8981,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
+    player->dir_waiting = player->MovDir;
     player->action_waiting = ACTION_DEFAULT;
 
     player->special_action_bored = ACTION_DEFAULT;
@@ -8841,26 +9127,72 @@ void GameActions()
 
   InitPlayfieldScanModeVars();
 
-  if (ScreenMovPos == 0)       /* screen currently aligned at tile position */
+#if 0
+  if (game.set_centered_player)
+  {
+    boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+    /* switching to "all players" only possible if all players fit to screen */
+    if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+    {
+      game.centered_player_nr_next = game.centered_player_nr;
+      game.set_centered_player = FALSE;
+    }
+
+    /* do not switch focus to non-existing (or non-active) player */
+    if (game.centered_player_nr_next >= 0 &&
+       !stored_player[game.centered_player_nr_next].active)
+    {
+      game.centered_player_nr_next = game.centered_player_nr;
+      game.set_centered_player = FALSE;
+    }
+  }
+
+  if (game.set_centered_player &&
+      ScreenMovPos == 0)       /* screen currently aligned at tile position */
   {
+#if 0
     struct PlayerInfo *player;
     int player_nr = game.centered_player_nr_next;
+#endif
+    int sx, sy;
 
     if (game.centered_player_nr_next == -1)
-      player_nr = local_player->index_nr;
+    {
+      setScreenCenteredToAllPlayers(&sx, &sy);
+    }
+    else
+    {
+      sx = stored_player[game.centered_player_nr_next].jx;
+      sy = stored_player[game.centered_player_nr_next].jy;
+    }
 
+#if 0
     player = &stored_player[player_nr];
 
     if (!player->active)
       game.centered_player_nr_next = game.centered_player_nr;
 
+    sx = player->jx;
+    sy = player->jy;
+#endif
+
+#if 0
     if (game.centered_player_nr != game.centered_player_nr_next)
+#endif
     {
+#if 1
+      DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+#else
       DrawRelocatePlayer(player, setup.quick_switch);
+#endif
 
       game.centered_player_nr = game.centered_player_nr_next;
     }
+
+    game.set_centered_player = FALSE;
   }
+#endif
 
 #if USE_ONE_MORE_CHANGE_PER_FRAME
   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
@@ -8895,6 +9227,79 @@ void GameActions()
 
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
+#if 1
+  if (tape.set_centered_player)
+  {
+    game.centered_player_nr_next = tape.centered_player_nr_next;
+    game.set_centered_player = TRUE;
+  }
+
+  if (game.set_centered_player)
+  {
+    boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
+
+    /* switching to "all players" only possible if all players fit to screen */
+    if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
+    {
+      game.centered_player_nr_next = game.centered_player_nr;
+      game.set_centered_player = FALSE;
+    }
+
+    /* do not switch focus to non-existing (or non-active) player */
+    if (game.centered_player_nr_next >= 0 &&
+       !stored_player[game.centered_player_nr_next].active)
+    {
+      game.centered_player_nr_next = game.centered_player_nr;
+      game.set_centered_player = FALSE;
+    }
+  }
+
+  if (game.set_centered_player &&
+      ScreenMovPos == 0)       /* screen currently aligned at tile position */
+  {
+#if 0
+    struct PlayerInfo *player;
+    int player_nr = game.centered_player_nr_next;
+#endif
+    int sx, sy;
+
+    if (game.centered_player_nr_next == -1)
+    {
+      setScreenCenteredToAllPlayers(&sx, &sy);
+    }
+    else
+    {
+      sx = stored_player[game.centered_player_nr_next].jx;
+      sy = stored_player[game.centered_player_nr_next].jy;
+    }
+
+#if 0
+    player = &stored_player[player_nr];
+
+    if (!player->active)
+      game.centered_player_nr_next = game.centered_player_nr;
+
+    sx = player->jx;
+    sy = player->jy;
+#endif
+
+#if 0
+    if (game.centered_player_nr != game.centered_player_nr_next)
+#endif
+    {
+#if 1
+      DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+#else
+      DrawRelocatePlayer(player, setup.quick_switch);
+#endif
+
+      game.centered_player_nr = game.centered_player_nr_next;
+    }
+
+    game.set_centered_player = FALSE;
+  }
+#endif
+
 #if 1
   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
   if (recorded_player_action == NULL && tape.pausing)