From 6f3de98756d06f95808903dccdc423085061912b Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Wed, 29 Jan 2025 16:03:33 +0100 Subject: [PATCH] tr1/savegame: fix death counter support Resolves #2412. --- docs/tr1/CHANGELOG.md | 1 + src/libtrx/game/phase/executor.c | 2 +- src/libtrx/game/savegame.c | 22 +++++++++++ src/libtrx/include/libtrx/game/savegame.h | 12 +++++- src/libtrx/meson.build | 1 + src/tr1/game/game/game.c | 4 +- src/tr1/game/game_flow/sequencer.c | 7 ++-- src/tr1/game/game_flow/sequencer_misc.c | 1 + src/tr1/game/inventory_ring/control.c | 8 ++-- src/tr1/game/lara/common.c | 6 +-- src/tr1/game/level.c | 4 -- src/tr1/game/option/option_passport.c | 12 +++--- src/tr1/game/savegame/savegame.c | 20 +++++----- src/tr1/game/savegame/savegame_bson.c | 47 +++++++---------------- src/tr1/game/savegame/savegame_legacy.c | 2 +- src/tr1/game/shell.c | 2 +- src/tr1/game/stats/common.c | 3 +- src/tr1/game/stats/types.h | 1 - src/tr1/game/ui/widgets/stats_dialog.c | 34 +++++++++------- src/tr1/global/types.h | 5 ++- src/tr2/decomp/savegame.c | 4 -- src/tr2/decomp/savegame.h | 2 + src/tr2/game/game_flow/sequencer_misc.c | 3 +- 23 files changed, 110 insertions(+), 93 deletions(-) create mode 100644 src/libtrx/game/savegame.c diff --git a/docs/tr1/CHANGELOG.md b/docs/tr1/CHANGELOG.md index 8045b734a..4637ec308 100644 --- a/docs/tr1/CHANGELOG.md +++ b/docs/tr1/CHANGELOG.md @@ -44,6 +44,7 @@ - fixed header and arrows disappearing when the inventory ring rotates (#2352, regression from 4.4) - fixed Story So Far feature not playing opening FMVs from the current level (#2360, regression from 4.2) - fixed `/play` command crashing the game when used after loading a level (#2411, regression) +- fixed various death counter problems (existing saves will have the count reset) (#2412, regression from 2.6) - fixed `/demo` command crashing the game if no demos are present (regression from 4.1) - fixed Lara not being able to jump or stop swimming if the related responsive config options are enabled, but enhanced animations are not present (#2397, regression from 4.6) - improved pause screen compatibility with PS1 (#2248) diff --git a/src/libtrx/game/phase/executor.c b/src/libtrx/game/phase/executor.c index 702759bae..6a26560c7 100644 --- a/src/libtrx/game/phase/executor.c +++ b/src/libtrx/game/phase/executor.c @@ -31,7 +31,7 @@ static PHASE_CONTROL M_Control(PHASE *const phase, const int32_t nframes) // A change in the game flow is not natural. Force features like death // counter to break from the currently active savegame file. - Savegame_ClearCurrentSlot(); + Savegame_UnbindSlot(); return (PHASE_CONTROL) { .action = PHASE_ACTION_END, .gf_cmd = gf_cmd }; } diff --git a/src/libtrx/game/savegame.c b/src/libtrx/game/savegame.c new file mode 100644 index 000000000..e6b29488c --- /dev/null +++ b/src/libtrx/game/savegame.c @@ -0,0 +1,22 @@ +#include "game/savegame.h" + +#include "log.h" + +static int32_t m_BoundSlot = -1; + +void Savegame_BindSlot(const int32_t slot_num) +{ + m_BoundSlot = slot_num; + LOG_DEBUG("Binding save slot %d", slot_num); +} + +void Savegame_UnbindSlot(void) +{ + LOG_DEBUG("Resetting the save slot"); + m_BoundSlot = -1; +} + +int32_t Savegame_GetBoundSlot(void) +{ + return m_BoundSlot; +} diff --git a/src/libtrx/include/libtrx/game/savegame.h b/src/libtrx/include/libtrx/game/savegame.h index 31ec2bd62..f2a4225f6 100644 --- a/src/libtrx/include/libtrx/game/savegame.h +++ b/src/libtrx/include/libtrx/game/savegame.h @@ -2,8 +2,18 @@ #include +// Remembers the slot used when the player starts a loaded game. +// Persists across level reloads. +void Savegame_BindSlot(int32_t slot_num); + +// Removes the binding of the current slot. Used when the player exits to +// title, issues a command like `/play` etc. +void Savegame_UnbindSlot(void); + +// Returns the currently bound slot number. If there is none, returns -1. +int32_t Savegame_GetBoundSlot(void); + extern int32_t Savegame_GetSlotCount(void); extern bool Savegame_IsSlotFree(int32_t slot_num); extern bool Savegame_Load(int32_t slot_num); extern bool Savegame_Save(int32_t slot_num); -extern void Savegame_ClearCurrentSlot(void); diff --git a/src/libtrx/meson.build b/src/libtrx/meson.build index afe6c4c67..8280444b1 100644 --- a/src/libtrx/meson.build +++ b/src/libtrx/meson.build @@ -150,6 +150,7 @@ sources = [ 'game/phase/phase_stats.c', 'game/random.c', 'game/rooms/common.c', + 'game/savegame.c', 'game/shell/common.c', 'game/sound.c', 'game/text.c', diff --git a/src/tr1/game/game/game.c b/src/tr1/game/game/game.c index 1f0ea7a98..db4184bab 100644 --- a/src/tr1/game/game/game.c +++ b/src/tr1/game/game/game.c @@ -117,7 +117,7 @@ GF_COMMAND Game_Stop_Legacy(void) if (g_GameInfo.passport_selection == PASSPORT_MODE_LOAD_GAME) { return (GF_COMMAND) { .action = GF_START_SAVED_GAME, - .param = g_GameInfo.current_save_slot, + .param = g_GameInfo.select_save_slot, }; } else if (g_GameInfo.passport_selection == PASSPORT_MODE_SELECT_LEVEL) { return (GF_COMMAND) { @@ -127,7 +127,7 @@ GF_COMMAND Game_Stop_Legacy(void) } else if (g_GameInfo.passport_selection == PASSPORT_MODE_STORY_SO_FAR) { return (GF_COMMAND) { .action = GF_STORY_SO_FAR, - .param = g_GameInfo.current_save_slot, + .param = g_GameInfo.select_save_slot, }; } else if (g_GameInfo.passport_selection == PASSPORT_MODE_RESTART) { return (GF_COMMAND) { diff --git a/src/tr1/game/game_flow/sequencer.c b/src/tr1/game/game_flow/sequencer.c index 0e32a8a9d..b4a9b6b88 100644 --- a/src/tr1/game/game_flow/sequencer.c +++ b/src/tr1/game/game_flow/sequencer.c @@ -89,13 +89,14 @@ static DECLARE_EVENT_HANDLER(M_HandleLoadLevel) // reset current info to the defaults so that we do not do // Item_GlobalReplace in the inventory initialization routines too early Savegame_InitCurrentInfo(); + const int16_t slot_num = Savegame_GetBoundSlot(); if (!Level_Initialise(level)) { Game_SetCurrentLevel(NULL); GF_SetCurrentLevel(NULL); return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } - if (!Savegame_Load(g_GameInfo.current_save_slot)) { + if (!Savegame_Load(slot_num)) { LOG_ERROR("Failed to load save file!"); Game_SetCurrentLevel(NULL); GF_SetCurrentLevel(NULL); @@ -119,12 +120,12 @@ static DECLARE_EVENT_HANDLER(M_HandleLoadLevel) break; case GFSC_SELECT: - if (g_GameInfo.current_save_slot != -1) { + if (Savegame_GetBoundSlot() != -1) { // select level feature Savegame_InitCurrentInfo(); if (level->num > GF_GetFirstLevel()->num) { Savegame_LoadOnlyResumeInfo( - g_GameInfo.current_save_slot, &g_GameInfo); + Savegame_GetBoundSlot(), &g_GameInfo); const GF_LEVEL *tmp_level = level; while (tmp_level != NULL) { Savegame_ResetCurrentInfo(tmp_level); diff --git a/src/tr1/game/game_flow/sequencer_misc.c b/src/tr1/game/game_flow/sequencer_misc.c index fae7403d6..ed0cd3148 100644 --- a/src/tr1/game/game_flow/sequencer_misc.c +++ b/src/tr1/game/game_flow/sequencer_misc.c @@ -10,6 +10,7 @@ GF_COMMAND GF_TitleSequence(void) { + Savegame_UnbindSlot(); GameStringTable_Apply(nullptr); const GF_LEVEL *const title_level = GF_GetTitleLevel(); if (!Level_Initialise(title_level)) { diff --git a/src/tr1/game/inventory_ring/control.c b/src/tr1/game/inventory_ring/control.c index bb505793a..6c6480fe0 100644 --- a/src/tr1/game/inventory_ring/control.c +++ b/src/tr1/game/inventory_ring/control.c @@ -250,7 +250,7 @@ static GF_COMMAND M_Finish(INV_RING *const ring, const bool apply_changes) case PASSPORT_MODE_LOAD_GAME: return (GF_COMMAND) { .action = GF_START_SAVED_GAME, - .param = g_GameInfo.current_save_slot, + .param = g_GameInfo.select_save_slot, }; case PASSPORT_MODE_SELECT_LEVEL: @@ -262,7 +262,7 @@ static GF_COMMAND M_Finish(INV_RING *const ring, const bool apply_changes) case PASSPORT_MODE_STORY_SO_FAR: return (GF_COMMAND) { .action = GF_STORY_SO_FAR, - .param = g_GameInfo.current_save_slot, + .param = g_GameInfo.select_save_slot, }; case PASSPORT_MODE_NEW_GAME: @@ -276,7 +276,7 @@ static GF_COMMAND M_Finish(INV_RING *const ring, const bool apply_changes) case PASSPORT_MODE_SAVE_GAME: if (apply_changes) { - Savegame_Save(g_GameInfo.current_save_slot); + Savegame_Save(g_GameInfo.select_save_slot); } return (GF_COMMAND) { .action = GF_NOOP }; @@ -300,7 +300,7 @@ static GF_COMMAND M_Finish(INV_RING *const ring, const bool apply_changes) case O_PHOTO_OPTION: if (apply_changes) { - Savegame_ClearCurrentSlot(); + Savegame_UnbindSlot(); } if (GF_GetGymLevel() != NULL) { return (GF_COMMAND) { diff --git a/src/tr1/game/lara/common.c b/src/tr1/game/lara/common.c index 8f21ac1cd..aaaaaa44e 100644 --- a/src/tr1/game/lara/common.c +++ b/src/tr1/game/lara/common.c @@ -273,10 +273,10 @@ void Lara_Control(void) item->hit_points = -1; if (!g_Lara.death_timer) { Music_Stop(); - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.death_count++; - if (g_GameInfo.current_save_slot != -1) { + g_GameInfo.death_count++; + if (Savegame_GetBoundSlot() != -1) { Savegame_UpdateDeathCounters( - g_GameInfo.current_save_slot, &g_GameInfo); + Savegame_GetBoundSlot(), &g_GameInfo); } } g_Lara.death_timer++; diff --git a/src/tr1/game/level.c b/src/tr1/game/level.c index 2d2983cdf..41f94b14a 100644 --- a/src/tr1/game/level.c +++ b/src/tr1/game/level.c @@ -974,9 +974,6 @@ bool Level_Initialise(const GF_LEVEL *const level) BENCHMARK *const benchmark = Benchmark_Start(); LOG_DEBUG("num=%d (%s)", level->num, level->path); - // loading a save can override it to false - g_GameInfo.death_counter_supported = true; - g_GameInfo.select_level_num = -1; const int32_t level_num = level->num; RESUME_INFO *const resume = GF_GetResumeInfo(level); @@ -986,7 +983,6 @@ bool Level_Initialise(const GF_LEVEL *const level) resume->stats.secret_count = 0; resume->stats.pickup_count = 0; resume->stats.kill_count = 0; - resume->stats.death_count = 0; } g_LevelComplete = false; diff --git a/src/tr1/game/option/option_passport.c b/src/tr1/game/option/option_passport.c index 25d4d0290..08e7d62be 100644 --- a/src/tr1/game/option/option_passport.c +++ b/src/tr1/game/option/option_passport.c @@ -382,7 +382,7 @@ static void M_ShowSaves(PASSPORT_MODE pending_mode) g_InputDB = (INPUT_STATE) {}; } else if (select > 0) { m_PassportStatus.mode = PASSPORT_MODE_BROWSE; - g_GameInfo.current_save_slot = select - 1; + g_GameInfo.select_save_slot = select - 1; g_GameInfo.passport_selection = pending_mode; } else if ( g_InvMode != INV_SAVE_MODE && g_InvMode != INV_SAVE_CRYSTAL_MODE @@ -400,7 +400,7 @@ static void M_ShowSelectLevel(void) int32_t select = Requester_Display(&m_SelectLevelRequester); if (select) { if (select - 1 + GF_GetFirstLevel()->num - == Savegame_GetLevelNumber(g_GameInfo.current_save_slot) + 1) { + == Savegame_GetLevelNumber(g_GameInfo.select_save_slot) + 1) { g_GameInfo.passport_selection = PASSPORT_MODE_STORY_SO_FAR; } else if (select > 0) { g_GameInfo.select_level_num = select - 1 + GF_GetFirstLevel()->num; @@ -435,7 +435,7 @@ static void M_LoadGame(void) if (!g_SavegameRequester.items[g_SavegameRequester.requested].is_blocked || !g_SavegameRequester.is_blockable) { if (g_InputDB.menu_right) { - g_GameInfo.current_save_slot = g_SavegameRequester.requested; + g_GameInfo.select_save_slot = g_SavegameRequester.requested; Text_Hide(m_Text[TEXT_LEVEL_ARROW_RIGHT], true); Requester_ClearTextstrings(&g_SavegameRequester); M_InitSelectLevelRequester(); @@ -534,7 +534,7 @@ static void M_NewGame(void) } else { g_GameInfo.save_initial_version = SAVEGAME_CURRENT_VERSION; g_GameInfo.bonus_level_unlock = false; - Savegame_ClearCurrentSlot(); + Savegame_UnbindSlot(); g_GameInfo.passport_selection = PASSPORT_MODE_NEW_GAME; } } else if (m_PassportStatus.mode == PASSPORT_MODE_NEW_GAME) { @@ -559,7 +559,7 @@ static void M_NewGame(void) break; } g_GameInfo.bonus_level_unlock = false; - Savegame_ClearCurrentSlot(); + Savegame_UnbindSlot(); g_GameInfo.passport_selection = PASSPORT_MODE_NEW_GAME; g_GameInfo.save_initial_version = SAVEGAME_CURRENT_VERSION; } else if ( @@ -580,7 +580,7 @@ static void M_Restart(INVENTORY_ITEM *inv_item) { M_ChangePageTextContent(GS(PASSPORT_RESTART_LEVEL)); - if (Savegame_RestartAvailable(g_GameInfo.current_save_slot)) { + if (Savegame_RestartAvailable(g_GameInfo.select_save_slot)) { if (g_InputDB.menu_confirm) { g_GameInfo.passport_selection = PASSPORT_MODE_RESTART; } diff --git a/src/tr1/game/savegame/savegame.c b/src/tr1/game/savegame/savegame.c index a32887238..603612156 100644 --- a/src/tr1/game/savegame/savegame.c +++ b/src/tr1/game/savegame/savegame.c @@ -226,6 +226,7 @@ void Savegame_ProcessItemsBeforeSave(void) void Savegame_InitCurrentInfo(void) { + g_GameInfo.death_count = 0; const GF_LEVEL_TABLE *const level_table = GF_GetLevelTable(GFLT_MAIN); for (int32_t i = 0; i < level_table->count; i++) { const GF_LEVEL *const level = &level_table->levels[i]; @@ -351,9 +352,9 @@ void Savegame_CarryCurrentInfoToNextLevel( LOG_INFO( "Copying resume info from level #%d to level #%d", src_level->num, dst_level->num); - memcpy( - GF_GetResumeInfo(dst_level), GF_GetResumeInfo(src_level), - sizeof(RESUME_INFO)); + RESUME_INFO *const src_resume = GF_GetResumeInfo(src_level); + RESUME_INFO *const dst_resume = GF_GetResumeInfo(dst_level); + memcpy(dst_resume, src_resume, sizeof(RESUME_INFO)); } void Savegame_PersistGameToCurrentInfo(const GF_LEVEL *const level) @@ -462,6 +463,7 @@ bool Savegame_Save(const int32_t slot_num) { GAME_INFO *const game_info = &g_GameInfo; bool ret = true; + Savegame_BindSlot(slot_num); File_CreateDirectory(SAVES_DIR); @@ -644,9 +646,12 @@ void Savegame_ScanSavedGames(void) void Savegame_ScanAvailableLevels(REQUEST_INFO *req) { - SAVEGAME_INFO *savegame_info = - &m_SavegameInfo[g_GameInfo.current_save_slot]; + const int32_t slot_num = Savegame_GetBoundSlot(); + if (slot_num == -1) { + return; + } + const SAVEGAME_INFO *const savegame_info = &m_SavegameInfo[slot_num]; if (!savegame_info->features.select_level) { Requester_AddItem(req, true, "%s", GS(PASSPORT_LEGACY_SELECT_LEVEL_1)); Requester_AddItem(req, true, "%s", GS(PASSPORT_LEGACY_SELECT_LEVEL_2)); @@ -686,8 +691,3 @@ bool Savegame_RestartAvailable(int32_t slot_num) SAVEGAME_INFO *savegame_info = &m_SavegameInfo[slot_num]; return savegame_info->features.restart; } - -void Savegame_ClearCurrentSlot(void) -{ - g_GameInfo.current_save_slot = -1; -} diff --git a/src/tr1/game/savegame/savegame_bson.c b/src/tr1/game/savegame/savegame_bson.c index 340f0e20e..ec0ee7602 100644 --- a/src/tr1/game/savegame/savegame_bson.c +++ b/src/tr1/game/savegame/savegame_bson.c @@ -263,8 +263,6 @@ static bool M_LoadResumeInfo(JSON_ARRAY *resume_arr, RESUME_INFO *resume_info) JSON_ObjectGetInt(resume_obj, "kills", resume->stats.kill_count); resume->stats.pickup_count = JSON_ObjectGetInt( resume_obj, "pickups", resume->stats.pickup_count); - resume->stats.death_count = - JSON_ObjectGetInt(resume_obj, "deaths", resume->stats.death_count); resume->stats.max_secret_count = JSON_ObjectGetInt( resume_obj, "max_secrets", resume->stats.max_secret_count); resume->stats.max_kill_count = JSON_ObjectGetInt( @@ -358,8 +356,6 @@ static bool M_LoadDiscontinuedEndInfo(JSON_ARRAY *end_arr, GAME_INFO *game_info) end->kill_count = JSON_ObjectGetInt(end_obj, "kills", end->kill_count); end->pickup_count = JSON_ObjectGetInt(end_obj, "pickups", end->pickup_count); - end->death_count = - JSON_ObjectGetInt(end_obj, "deaths", end->death_count); end->max_secret_count = JSON_ObjectGetInt(end_obj, "max_secrets", end->max_secret_count); end->max_kill_count = @@ -367,7 +363,6 @@ static bool M_LoadDiscontinuedEndInfo(JSON_ARRAY *end_arr, GAME_INFO *game_info) end->max_pickup_count = JSON_ObjectGetInt(end_obj, "max_pickups", end->max_pickup_count); } - game_info->death_counter_supported = true; return true; } @@ -383,6 +378,7 @@ static bool M_LoadMisc( if (header_version >= VERSION_4) { game_info->bonus_level_unlock = JSON_ObjectGetBool(misc_obj, "bonus_level_unlock", 0); + game_info->death_count = JSON_ObjectGetInt(misc_obj, "death_count", -1); } return true; } @@ -971,7 +967,6 @@ static JSON_ARRAY *M_DumpResumeInfo(RESUME_INFO *resume_info) JSON_ObjectAppendInt(resume_obj, "kills", resume->stats.kill_count); JSON_ObjectAppendInt(resume_obj, "secrets", resume->stats.secret_flags); JSON_ObjectAppendInt(resume_obj, "pickups", resume->stats.pickup_count); - JSON_ObjectAppendInt(resume_obj, "deaths", resume->stats.death_count); JSON_ObjectAppendInt( resume_obj, "max_kills", resume->stats.max_kill_count); JSON_ObjectAppendInt( @@ -990,6 +985,7 @@ static JSON_OBJECT *M_DumpMisc(GAME_INFO *game_info) JSON_ObjectAppendInt(misc_obj, "bonus_flag", game_info->bonus_flag); JSON_ObjectAppendBool( misc_obj, "bonus_level_unlock", game_info->bonus_level_unlock); + JSON_ObjectAppendInt(misc_obj, "death_count", game_info->death_count); return misc_obj; } @@ -1480,45 +1476,28 @@ void Savegame_BSON_SaveToFile(MYFILE *fp, GAME_INFO *game_info) bool Savegame_BSON_UpdateDeathCounters(MYFILE *fp, GAME_INFO *game_info) { - bool ret = false; + bool result = false; int32_t version; - JSON_VALUE *root = M_ParseFromFile(fp, &version); - JSON_OBJECT *root_obj = JSON_ValueAsObject(root); - if (!root_obj) { + JSON_VALUE *const root = M_ParseFromFile(fp, &version); + JSON_OBJECT *const root_obj = JSON_ValueAsObject(root); + if (root_obj == nullptr) { LOG_ERROR("Cannot find the root object"); goto cleanup; } - JSON_ARRAY *current_arr = JSON_ObjectGetArray(root_obj, "current_info"); - if (!current_arr) { - current_arr = JSON_ObjectGetArray(root_obj, "end_info"); - if (!current_arr) { - LOG_ERROR("Malformed save: invalid or missing current info array"); - goto cleanup; - } - } - if ((signed)current_arr->length != GF_GetLevelTable(GFLT_MAIN)->count) { - LOG_ERROR( - "Malformed save: expected %d current info elements, got %d", - GF_GetLevelTable(GFLT_MAIN)->count, current_arr->length); + JSON_OBJECT *const misc_obj = JSON_ObjectGetObject(root_obj, "misc"); + if (misc_obj == nullptr) { + LOG_ERROR("Cannot find the misc object"); goto cleanup; } - for (int i = 0; i < (signed)current_arr->length; i++) { - JSON_OBJECT *cur_obj = JSON_ArrayGetObject(current_arr, i); - if (!cur_obj) { - LOG_ERROR("Malformed save: invalid current info"); - goto cleanup; - } - RESUME_INFO *current = &game_info->current[i]; - JSON_ObjectEvictKey(cur_obj, "deaths"); - JSON_ObjectAppendInt(cur_obj, "deaths", current->stats.death_count); - } + JSON_ObjectEvictKey(misc_obj, "death_count"); + JSON_ObjectAppendInt(misc_obj, "death_count", game_info->death_count); File_Seek(fp, 0, FILE_SEEK_SET); M_SaveRaw(fp, root, version); - ret = true; + result = true; cleanup: JSON_ValueFree(root); - return ret; + return result; } diff --git a/src/tr1/game/savegame/savegame_legacy.c b/src/tr1/game/savegame/savegame_legacy.c index 49d1dbc8b..46efbe04a 100644 --- a/src/tr1/game/savegame/savegame_legacy.c +++ b/src/tr1/game/savegame/savegame_legacy.c @@ -479,7 +479,7 @@ static void M_ReadResumeInfo(MYFILE *fp, GAME_INFO *game_info) Stats_UpdateSecrets(&resume_info->stats); M_Read(&resume_info->stats.pickup_count, sizeof(uint8_t)); M_Read(&game_info->bonus_flag, sizeof(uint8_t)); - game_info->death_counter_supported = false; + game_info->death_count = -1; } char *Savegame_Legacy_GetSaveFileName(int32_t slot) diff --git a/src/tr1/game/shell.c b/src/tr1/game/shell.c index a964483b9..d048e7483 100644 --- a/src/tr1/game/shell.c +++ b/src/tr1/game/shell.c @@ -220,7 +220,7 @@ void Shell_Main(void) LOG_ERROR("Corrupt save file!"); gf_cmd = (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } else { - g_GameInfo.current_save_slot = slot_num; + Savegame_BindSlot(slot_num); const GF_LEVEL *const level = GF_GetLevel(GFLT_MAIN, level_num); gf_cmd = GF_InterpretSequence(level, GFSC_SAVED, NULL); } diff --git a/src/tr1/game/stats/common.c b/src/tr1/game/stats/common.c index 1552e6144..1ccc5802e 100644 --- a/src/tr1/game/stats/common.c +++ b/src/tr1/game/stats/common.c @@ -129,13 +129,12 @@ void Stats_ComputeFinal(GF_LEVEL_TYPE level_type, FINAL_STATS *final_stats) if (level->type != level_type) { continue; } - const LEVEL_STATS *level_stats = &g_GameInfo.current[i].stats; + const LEVEL_STATS *level_stats = &GF_GetResumeInfo(level)->stats; final_stats->kill_count += level_stats->kill_count; final_stats->pickup_count += level_stats->pickup_count; final_stats->secret_count += level_stats->secret_count; final_stats->timer += level_stats->timer; - final_stats->death_count += level_stats->death_count; final_stats->max_kill_count += level_stats->max_kill_count; final_stats->max_secret_count += level_stats->max_secret_count; final_stats->max_pickup_count += level_stats->max_pickup_count; diff --git a/src/tr1/game/stats/types.h b/src/tr1/game/stats/types.h index 45d57b3f9..a0fe6c34a 100644 --- a/src/tr1/game/stats/types.h +++ b/src/tr1/game/stats/types.h @@ -4,7 +4,6 @@ typedef struct STATS_COMMON { uint32_t timer; - uint32_t death_count; uint32_t kill_count; uint16_t secret_count; uint16_t pickup_count; diff --git a/src/tr1/game/ui/widgets/stats_dialog.c b/src/tr1/game/ui/widgets/stats_dialog.c index c8f0d9cd0..587ccabf8 100644 --- a/src/tr1/game/ui/widgets/stats_dialog.c +++ b/src/tr1/game/ui/widgets/stats_dialog.c @@ -53,8 +53,11 @@ static const char *M_GetDialogTitle(UI_STATS_DIALOG *self); static void M_AddRow( UI_STATS_DIALOG *self, M_ROW_ROLE role, const char *key, const char *value); static void M_AddRowFromRole( - UI_STATS_DIALOG *self, M_ROW_ROLE role, const STATS_COMMON *stats); -static void M_AddCommonRows(UI_STATS_DIALOG *self, const STATS_COMMON *stats); + UI_STATS_DIALOG *self, M_ROW_ROLE role, const STATS_COMMON *stats, + const GAME_INFO *game_info); +static void M_AddCommonRows( + UI_STATS_DIALOG *self, const STATS_COMMON *stats, + const GAME_INFO *game_info); static void M_AddLevelStatsRows(UI_STATS_DIALOG *self); static void M_AddFinalStatsRows(UI_STATS_DIALOG *self); static void M_UpdateTimerRow(UI_STATS_DIALOG *self); @@ -134,7 +137,7 @@ static void M_AddRow( static void M_AddRowFromRole( UI_STATS_DIALOG *const self, const M_ROW_ROLE role, - const STATS_COMMON *const stats) + const STATS_COMMON *const stats, const GAME_INFO *const game_info) { char buf[50]; const char *const num_fmt = g_Config.gameplay.enable_detailed_stats @@ -160,7 +163,7 @@ static void M_AddRowFromRole( break; case M_ROW_DEATHS: - sprintf(buf, "%d", stats->death_count); + sprintf(buf, "%d", game_info->death_count); M_AddRow(self, role, GS(STATS_DEATHS), buf); break; @@ -175,30 +178,35 @@ static void M_AddRowFromRole( } static void M_AddCommonRows( - UI_STATS_DIALOG *const self, const STATS_COMMON *const stats) + UI_STATS_DIALOG *const self, const STATS_COMMON *const stats, + const GAME_INFO *const game_info) { - M_AddRowFromRole(self, M_ROW_KILLS, stats); - M_AddRowFromRole(self, M_ROW_PICKUPS, stats); - M_AddRowFromRole(self, M_ROW_SECRETS, stats); + M_AddRowFromRole(self, M_ROW_KILLS, stats, game_info); + M_AddRowFromRole(self, M_ROW_PICKUPS, stats, game_info); + M_AddRowFromRole(self, M_ROW_SECRETS, stats, game_info); if (g_Config.gameplay.enable_deaths_counter - && g_GameInfo.death_counter_supported) { - M_AddRowFromRole(self, M_ROW_DEATHS, stats); + && game_info->death_count >= 0) { + // Always use sum of all levels for the deaths. + // Deaths get stored in the resume info for the level they happen on, + // so if the player dies in Vilcabamba and reloads Caves, they should + // still see an incremented death counter. + M_AddRowFromRole(self, M_ROW_DEATHS, stats, game_info); } - M_AddRowFromRole(self, M_ROW_TIMER, stats); + M_AddRowFromRole(self, M_ROW_TIMER, stats, game_info); } static void M_AddLevelStatsRows(UI_STATS_DIALOG *const self) { const STATS_COMMON *stats = (STATS_COMMON *)&g_GameInfo.current[self->args.level_num].stats; - M_AddCommonRows(self, stats); + M_AddCommonRows(self, stats, &g_GameInfo); } static void M_AddFinalStatsRows(UI_STATS_DIALOG *const self) { FINAL_STATS final_stats; Stats_ComputeFinal(self->level_type, &final_stats); - M_AddCommonRows(self, (STATS_COMMON *)&final_stats); + M_AddCommonRows(self, (STATS_COMMON *)&final_stats, &g_GameInfo); } static void M_UpdateTimerRow(UI_STATS_DIALOG *const self) diff --git a/src/tr1/global/types.h b/src/tr1/global/types.h index b4d7f1de1..6e975e10d 100644 --- a/src/tr1/global/types.h +++ b/src/tr1/global/types.h @@ -217,13 +217,14 @@ typedef struct { typedef struct { RESUME_INFO *current; + int32_t death_count; + uint8_t bonus_flag; bool bonus_level_unlock; - int32_t current_save_slot; int16_t save_initial_version; PASSPORT_MODE passport_selection; + int32_t select_save_slot; int32_t select_level_num; - bool death_counter_supported; bool remove_guns; bool remove_scions; bool remove_ammo; diff --git a/src/tr2/decomp/savegame.c b/src/tr2/decomp/savegame.c index 2054ce015..4b486cbb6 100644 --- a/src/tr2/decomp/savegame.c +++ b/src/tr2/decomp/savegame.c @@ -1093,7 +1093,3 @@ int32_t S_LoadGame(const int32_t slot_num) File_Close(fp); return true; } - -void Savegame_ClearCurrentSlot(void) -{ -} diff --git a/src/tr2/decomp/savegame.h b/src/tr2/decomp/savegame.h index d106c2793..9f2d0789a 100644 --- a/src/tr2/decomp/savegame.h +++ b/src/tr2/decomp/savegame.h @@ -3,6 +3,8 @@ #include "game/game_flow/types.h" #include "global/types.h" +#include + #include void Savegame_ResetCurrentInfo(const GF_LEVEL *level); diff --git a/src/tr2/game/game_flow/sequencer_misc.c b/src/tr2/game/game_flow/sequencer_misc.c index 2683a6d1a..2173170ad 100644 --- a/src/tr2/game/game_flow/sequencer_misc.c +++ b/src/tr2/game/game_flow/sequencer_misc.c @@ -1,4 +1,4 @@ -#include "game/demo.h" +#include "decomp/savegame.h" #include "game/game.h" #include "game/game_flow/common.h" #include "game/game_flow/sequencer.h" @@ -12,6 +12,7 @@ GF_COMMAND GF_TitleSequence(void) { + Savegame_UnbindSlot(); GameStringTable_Apply(NULL); const GF_LEVEL *const title_level = GF_GetTitleLevel(); if (!Level_Initialise(title_level, GFSC_NORMAL)) {