mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
parent
fb26660da1
commit
69f73edb08
23 changed files with 118 additions and 97 deletions
|
@ -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)
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
|
22
src/libtrx/game/savegame.c
Normal file
22
src/libtrx/game/savegame.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -2,8 +2,18 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
// 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);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,12 +262,13 @@ 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:
|
||||
if (apply_changes) {
|
||||
Savegame_InitCurrentInfo();
|
||||
Savegame_UnbindSlot();
|
||||
}
|
||||
return (GF_COMMAND) {
|
||||
.action = GF_START_GAME,
|
||||
|
@ -276,7 +277,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 +301,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) {
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
@ -487,14 +487,19 @@ static void M_SelectLevel(void)
|
|||
} else {
|
||||
M_ShowSelectLevel();
|
||||
if (m_PassportStatus.mode == PASSPORT_MODE_SELECT_LEVEL) {
|
||||
Text_SetPos(
|
||||
m_Text[TEXT_LEVEL_ARROW_LEFT], -130,
|
||||
const TEXTSTRING *const sel_item =
|
||||
m_SelectLevelRequester
|
||||
.items
|
||||
[m_SelectLevelRequester.requested
|
||||
- m_SelectLevelRequester.line_offset]
|
||||
.content->pos.y);
|
||||
Text_Hide(m_Text[TEXT_LEVEL_ARROW_LEFT], false);
|
||||
.content;
|
||||
if (sel_item != nullptr) {
|
||||
Text_SetPos(
|
||||
m_Text[TEXT_LEVEL_ARROW_LEFT], -130, sel_item->pos.y);
|
||||
Text_Hide(m_Text[TEXT_LEVEL_ARROW_LEFT], false);
|
||||
} else {
|
||||
Text_Hide(m_Text[TEXT_LEVEL_ARROW_LEFT], true);
|
||||
}
|
||||
} else {
|
||||
Text_Hide(m_Text[TEXT_LEVEL_ARROW_LEFT], true);
|
||||
}
|
||||
|
@ -534,7 +539,6 @@ static void M_NewGame(void)
|
|||
} else {
|
||||
g_GameInfo.save_initial_version = SAVEGAME_CURRENT_VERSION;
|
||||
g_GameInfo.bonus_level_unlock = false;
|
||||
Savegame_ClearCurrentSlot();
|
||||
g_GameInfo.passport_selection = PASSPORT_MODE_NEW_GAME;
|
||||
}
|
||||
} else if (m_PassportStatus.mode == PASSPORT_MODE_NEW_GAME) {
|
||||
|
@ -559,7 +563,6 @@ static void M_NewGame(void)
|
|||
break;
|
||||
}
|
||||
g_GameInfo.bonus_level_unlock = false;
|
||||
Savegame_ClearCurrentSlot();
|
||||
g_GameInfo.passport_selection = PASSPORT_MODE_NEW_GAME;
|
||||
g_GameInfo.save_initial_version = SAVEGAME_CURRENT_VERSION;
|
||||
} else if (
|
||||
|
@ -580,7 +583,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;
|
||||
}
|
||||
|
|
|
@ -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 = g_GameInfo.select_save_slot;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1093,7 +1093,3 @@ int32_t S_LoadGame(const int32_t slot_num)
|
|||
File_Close(fp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Savegame_ClearCurrentSlot(void)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
#include "game/game_flow/types.h"
|
||||
#include "global/types.h"
|
||||
|
||||
#include <libtrx/game/savegame.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void Savegame_ResetCurrentInfo(const GF_LEVEL *level);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue