fixed bug with not updating game panel when leaving invisible warp mode
[rocksndiamonds.git] / src / game.c
index edf7f6ab5066ef7af0900bcdcac720f386b7e4af..d8a3d16270af01b54ed8dcff09072d13f79aff4a 100644 (file)
@@ -1119,6 +1119,8 @@ void ExitPlayer(struct PlayerInfo *);
 static int getInvisibleActiveFromInvisibleElement(int);
 static int getInvisibleFromInvisibleActiveElement(int);
 
+static void TestFieldAfterSnapping(int, int, int, int, int);
+
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
 // for detection of endless loops, caused by custom element programming
@@ -2867,12 +2869,10 @@ void UpdateAndDisplayGameControlValues(void)
   DisplayGameControlValues();
 }
 
-#if 0
-static void UpdateGameDoorValues(void)
+void UpdateGameDoorValues(void)
 {
   UpdateGameControlValues();
 }
-#endif
 
 void DrawGameDoorValues(void)
 {
@@ -3576,11 +3576,15 @@ void InitGame(void)
   InitGameEngine();
   InitGameControlValues();
 
-  // initialize tape actions from game when recording tape
   if (tape.recording)
   {
+    // initialize tape actions from game when recording tape
     tape.use_key_actions   = game.use_key_actions;
     tape.use_mouse_actions = game.use_mouse_actions;
+
+    // initialize visible playfield size when recording tape (for team mode)
+    tape.scr_fieldx = SCR_FIELDX;
+    tape.scr_fieldy = SCR_FIELDY;
   }
 
   // don't play tapes over network
@@ -3713,6 +3717,8 @@ void InitGame(void)
     player->shield_normal_time_left = 0;
     player->shield_deadly_time_left = 0;
 
+    player->last_removed_element = EL_UNDEFINED;
+
     player->inventory_infinite_element = EL_UNDEFINED;
     player->inventory_size = 0;
 
@@ -4447,7 +4453,9 @@ void InitGame(void)
 
   game.restart_level = FALSE;
   game.restart_game_message = NULL;
+
   game.request_active = FALSE;
+  game.request_active_or_moving = FALSE;
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
     InitGameActions_MM();
@@ -4708,7 +4716,7 @@ void GameWon(void)
 {
   static int time_count_steps;
   static int time, time_final;
-  static int score, score_final;
+  static float score, score_final; // needed for time score < 10 for 10 seconds
   static int health, health_final;
   static int game_over_delay_1 = 0;
   static int game_over_delay_2 = 0;
@@ -4716,6 +4724,8 @@ void GameWon(void)
   int game_over_delay_value_1 = 50;
   int game_over_delay_value_2 = 25;
   int game_over_delay_value_3 = 50;
+  int time_score_base = MIN(MAX(1, level.time_score_base), 10);
+  float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
 
   if (!game.LevelSolved_GameWon)
   {
@@ -4749,19 +4759,23 @@ void GameWon(void)
     score = score_final = game.score_final;
     health = health_final = game.health_final;
 
-    if (level.score[SC_TIME_BONUS] > 0)
+    if (time_score > 0)
     {
+      int time_frames = 0;
+
       if (TimeLeft > 0)
       {
        time_final = 0;
-       score_final += TimeLeft * level.score[SC_TIME_BONUS];
+       time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
       }
       else if (game.no_time_limit && TimePlayed < 999)
       {
        time_final = 999;
-       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
+       time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
       }
 
+      score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
+
       time_count_steps = MAX(1, ABS(time_final - time) / 100);
 
       game_over_delay_1 = game_over_delay_value_1;
@@ -4769,7 +4783,7 @@ void GameWon(void)
       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
       {
        health_final = 0;
-       score_final += health * level.score[SC_TIME_BONUS];
+       score_final += health * time_score;
 
        game_over_delay_2 = game_over_delay_value_2;
       }
@@ -4858,7 +4872,11 @@ void GameWon(void)
       time_count_steps = 1;
 
     time  += time_count_steps * time_count_dir;
-    score += time_count_steps * level.score[SC_TIME_BONUS];
+    score += time_count_steps * time_score;
+
+    // set final score to correct rounding differences after counting score
+    if (time == time_final)
+      score = score_final;
 
     game.LevelSolved_CountingTime = time;
     game.LevelSolved_CountingScore = score;
@@ -4890,7 +4908,7 @@ void GameWon(void)
     int health_count_dir = (health < health_final ? +1 : -1);
 
     health += health_count_dir;
-    score  += level.score[SC_TIME_BONUS];
+    score  += time_score;
 
     game.LevelSolved_CountingHealth = health;
     game.LevelSolved_CountingScore = score;
@@ -11280,13 +11298,14 @@ static void CheckSingleStepMode(struct PlayerInfo *player)
 {
   if (tape.single_step && tape.recording && !tape.pausing)
   {
-    /* as it is called "single step mode", just return to pause mode when the
-       player stopped moving after one tile (or never starts moving at all) */
-    if (!player->is_moving &&
-       !player->is_pushing &&
-       !player->is_dropping_pressed &&
-       !player->effective_mouse_action.button)
-      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+    // as it is called "single step mode", just return to pause mode when the
+    // player stopped moving after one tile (or never starts moving at all)
+    // (reverse logic needed here in case single step mode used in team mode)
+    if (player->is_moving ||
+       player->is_pushing ||
+       player->is_dropping_pressed ||
+       player->effective_mouse_action.button)
+      game.enter_single_step_mode = FALSE;
   }
 
   CheckSaveEngineSnapshot(player);
@@ -11949,6 +11968,10 @@ void GameActions_RND(void)
     DrawGameDoorValues();
   }
 
+  // check single step mode (set flag and clear again if any player is active)
+  game.enter_single_step_mode =
+    (tape.single_step && tape.recording && !tape.pausing);
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     int actual_player_action = stored_player[i].effective_action;
@@ -11973,6 +11996,10 @@ void GameActions_RND(void)
     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
   }
 
+  // single step pause mode may already have been toggled by "ScrollPlayer()"
+  if (game.enter_single_step_mode && !tape.pausing)
+    TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
   ScrollScreen(NULL, SCROLL_GO_ON);
 
   /* for backwards compatibility, the following code emulates a fixed bug that
@@ -12025,10 +12052,17 @@ void GameActions_RND(void)
       MovDelay[x][y]--;
       if (MovDelay[x][y] <= 0)
       {
+       int element = Store[x][y];
+       int move_direction = MovDir[x][y];
+       int player_index_bit = Store2[x][y];
+
+       Store[x][y] = 0;
+       Store2[x][y] = 0;
+
        RemoveField(x, y);
        TEST_DrawLevelField(x, y);
 
-       TestIfElementTouchesCustomElement(x, y);        // for empty space
+       TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
       }
     }
 
@@ -12446,6 +12480,8 @@ void GameActions_RND(void)
 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
 {
   int min_x = x, min_y = y, max_x = x, max_y = y;
+  int scr_fieldx = getScreenFieldSizeX();
+  int scr_fieldy = getScreenFieldSizeY();
   int i;
 
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -12461,7 +12497,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
     max_y = MAX(max_y, jy);
   }
 
-  return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
+  return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
 }
 
 static boolean AllPlayersInVisibleScreen(void)
@@ -13027,6 +13063,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       if (!player->is_pushing)
        TestIfElementTouchesCustomElement(jx, jy);      // for empty space
 
+      if (level.finish_dig_collect &&
+         (player->is_digging || player->is_collecting))
+      {
+       int last_element = player->last_removed_element;
+       int move_direction = player->MovDir;
+       int enter_side = MV_DIR_OPPOSITE(move_direction);
+       int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
+                           CE_PLAYER_COLLECTS_X);
+
+       CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
+                                           player->index_bit, enter_side);
+
+       player->last_removed_element = EL_UNDEFINED;
+      }
+
       if (!player->active)
        RemovePlayer(player);
     }
@@ -13761,7 +13812,8 @@ void ExitPlayer(struct PlayerInfo *player)
     game.players_still_needed--;
 }
 
-static void setFieldForSnapping(int x, int y, int element, int direction)
+static void SetFieldForSnapping(int x, int y, int element, int direction,
+                               int player_index_bit)
 {
   struct ElementInfo *ei = &element_info[element];
   int direction_bit = MV_DIR_TO_BIT(direction);
@@ -13771,6 +13823,9 @@ static void setFieldForSnapping(int x, int y, int element, int direction)
 
   Tile[x][y] = EL_ELEMENT_SNAPPING;
   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
+  MovDir[x][y] = direction;
+  Store[x][y] = element;
+  Store2[x][y] = player_index_bit;
 
   ResetGfxAnimation(x, y);
 
@@ -13780,6 +13835,20 @@ static void setFieldForSnapping(int x, int y, int element, int direction)
   GfxFrame[x][y] = -1;
 }
 
+static void TestFieldAfterSnapping(int x, int y, int element, int direction,
+                                  int player_index_bit)
+{
+  TestIfElementTouchesCustomElement(x, y);     // for empty space
+
+  if (level.finish_dig_collect)
+  {
+    int dig_side = MV_DIR_OPPOSITE(direction);
+
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                       player_index_bit, dig_side);
+  }
+}
+
 /*
   =============================================================================
   checkDiagonalPushing()
@@ -14059,22 +14128,28 @@ static int DigField(struct PlayerInfo *player,
 
     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
-                                       player->index_bit, dig_side);
+    // use old behaviour for old levels (digging)
+    if (!level.finish_dig_collect)
+    {
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
+                                         player->index_bit, dig_side);
 
-    // if digging triggered player relocation, finish digging tile
-    if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
-      setFieldForSnapping(x, y, element, move_direction);
+      // if digging triggered player relocation, finish digging tile
+      if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
+    }
 
     if (mode == DF_SNAP)
     {
       if (level.block_snap_field)
-       setFieldForSnapping(x, y, element, move_direction);
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
       else
-       TestIfElementTouchesCustomElement(x, y);        // for empty space
+       TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
 
-      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
-                                         player->index_bit, dig_side);
+      // use old behaviour for old levels (snapping)
+      if (!level.finish_dig_collect)
+       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                           player->index_bit, dig_side);
     }
   }
   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
@@ -14186,25 +14261,28 @@ static int DigField(struct PlayerInfo *player,
     RaiseScoreElement(element);
     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
-    if (is_player)
+    // use old behaviour for old levels (collecting)
+    if (!level.finish_dig_collect && is_player)
     {
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
                                          player->index_bit, dig_side);
 
       // if collecting triggered player relocation, finish collecting tile
       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
-       setFieldForSnapping(x, y, element, move_direction);
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
     }
 
     if (mode == DF_SNAP)
     {
       if (level.block_snap_field)
-       setFieldForSnapping(x, y, element, move_direction);
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
       else
-       TestIfElementTouchesCustomElement(x, y);        // for empty space
+       TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
 
-      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
-                                         player->index_bit, dig_side);
+      // use old behaviour for old levels (snapping)
+      if (!level.finish_dig_collect)
+       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                           player->index_bit, dig_side);
     }
   }
   else if (player_can_move_or_snap && IS_PUSHABLE(element))
@@ -14542,6 +14620,8 @@ static int DigField(struct PlayerInfo *player,
     {
       player->is_collecting = !player->is_digging;
       player->is_active = TRUE;
+
+      player->last_removed_element = element;
     }
   }