diff --git a/data/tr1/ship/cfg/TR1X_gameflow.json5 b/data/tr1/ship/cfg/TR1X_gameflow.json5 index e83ba7e01..5d200d142 100644 --- a/data/tr1/ship/cfg/TR1X_gameflow.json5 +++ b/data/tr1/ship/cfg/TR1X_gameflow.json5 @@ -417,7 +417,6 @@ {"type": "loading_screen", "path": "data/images/peru.webp", "fade_in_time": 1.0, "fade_out_time": 1.0}, {"type": "load_level"}, {"type": "play_level"}, - {"type": "level_stats"}, {"type": "level_complete"}, ], }, @@ -436,7 +435,6 @@ {"type": "loading_screen", "path": "data/images/peru.webp", "fade_in_time": 1.0, "fade_out_time": 1.0}, {"type": "load_level"}, {"type": "play_level"}, - {"type": "level_stats"}, {"type": "level_complete"}, ], }, diff --git a/src/libtrx/engine/video.c b/src/libtrx/engine/video.c index 03fde7ed4..adc93fe61 100644 --- a/src/libtrx/engine/video.c +++ b/src/libtrx/engine/video.c @@ -267,7 +267,7 @@ typedef struct { } M_STATE; static int64_t m_AudioCallbackTime; -static SDL_AudioDeviceID m_AudioDevice; +static SDL_AudioDeviceID m_AudioDevice = 0; static int M_PacketQueuePutPrivate(M_PACKET_QUEUE *q, AVPacket *pkt) { @@ -778,10 +778,13 @@ static void M_StreamComponentClose(M_STATE *is, int stream_index) switch (codecpar->codec_type) { case AVMEDIA_TYPE_AUDIO: M_DecoderAbort(&is->auddec, &is->sampq); - SDL_CloseAudioDevice(m_AudioDevice); M_DecoderShutdown(&is->auddec); swr_free(&is->swr_ctx); av_freep(&is->audio_buf1); + if (m_AudioDevice > 0) { + SDL_CloseAudioDevice(m_AudioDevice); + m_AudioDevice = 0; + } is->audio_buf1_size = 0; is->audio_buf = nullptr; diff --git a/src/libtrx/include/libtrx/game/game_flow/sequencer.h b/src/libtrx/include/libtrx/game/game_flow/sequencer.h index 96dc7753f..3063b0551 100644 --- a/src/libtrx/include/libtrx/game/game_flow/sequencer.h +++ b/src/libtrx/include/libtrx/game/game_flow/sequencer.h @@ -7,12 +7,12 @@ GF_COMMAND GF_EnterPhotoMode(void); GF_COMMAND GF_PauseGame(void); GF_COMMAND GF_ShowInventory(INVENTORY_MODE inv_mode); GF_COMMAND GF_ShowInventoryKeys(GAME_OBJECT_ID receptacle_type_id); +GF_COMMAND GF_RunTitle(void); GF_COMMAND GF_RunDemo(int32_t demo_num); GF_COMMAND GF_RunCutscene(int32_t cutscene_num); GF_COMMAND GF_RunGame(const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx); GF_COMMAND GF_DoFrontendSequence(void); -GF_COMMAND GF_TitleSequence(void); GF_COMMAND GF_DoDemoSequence(int32_t demo_num); GF_COMMAND GF_DoCutsceneSequence(int32_t cutscene_num); diff --git a/src/tr1/game/cutscene.c b/src/tr1/game/cutscene.c index da1a4f2d7..1916df64f 100644 --- a/src/tr1/game/cutscene.c +++ b/src/tr1/game/cutscene.c @@ -18,10 +18,9 @@ #include "global/types.h" #include "global/vars.h" +#include #include -#include - static void M_InitialiseLara(const GF_LEVEL *level); static void M_InitialiseLara(const GF_LEVEL *const level) @@ -60,11 +59,7 @@ static void M_InitialiseLara(const GF_LEVEL *const level) bool Cutscene_Start(const int32_t level_num) { const GF_LEVEL *const level = GF_GetLevel(GFLT_CUTSCENES, level_num); - if (GF_GetCurrentLevel()->num != level_num) { - if (!Level_Initialise(level)) { - return false; - } - } + ASSERT(GF_GetCurrentLevel() == level); M_InitialiseLara(level); diff --git a/src/tr1/game/demo.c b/src/tr1/game/demo.c index 9d48da7ac..30cd61316 100644 --- a/src/tr1/game/demo.c +++ b/src/tr1/game/demo.c @@ -17,11 +17,13 @@ #include "game/phase.h" #include "game/random.h" #include "game/room.h" +#include "game/savegame.h" #include "game/shell.h" #include "game/sound.h" #include "global/vars.h" #include +#include #include #define MODIFY_CONFIG() \ @@ -37,7 +39,6 @@ typedef struct { uint32_t *demo_ptr; const GF_LEVEL *level; CONFIG old_config; - RESUME_INFO old_resume_info; TEXTSTRING *text; } M_PRIV; @@ -46,8 +47,6 @@ static M_PRIV m_Priv; static void M_PrepareConfig(M_PRIV *const p); static void M_RestoreConfig(M_PRIV *const p); -static void M_PrepareResumeInfo(M_PRIV *const p); -static void M_RestoreResumeInfo(M_PRIV *const p); static bool M_ProcessInput(M_PRIV *const p); static void M_PrepareConfig(M_PRIV *const p) @@ -67,36 +66,6 @@ static void M_RestoreConfig(M_PRIV *const p) MODIFY_CONFIG(); } -static void M_PrepareResumeInfo(M_PRIV *const p) -{ - RESUME_INFO *const resume_info = GF_GetResumeInfo(p->level); - p->old_resume_info = *resume_info; - resume_info->flags.available = 1; - resume_info->flags.costume = 0; - resume_info->num_medis = 0; - resume_info->num_big_medis = 0; - resume_info->num_scions = 0; - resume_info->flags.got_pistols = 1; - resume_info->flags.got_shotgun = 0; - resume_info->flags.got_magnums = 0; - resume_info->flags.got_uzis = 0; - resume_info->pistol_ammo = 1000; - resume_info->shotgun_ammo = 0; - resume_info->magnum_ammo = 0; - resume_info->uzi_ammo = 0; - resume_info->gun_status = LGS_ARMLESS; - resume_info->equipped_gun_type = LGT_PISTOLS; - resume_info->holsters_gun_type = LGT_PISTOLS; - resume_info->back_gun_type = LGT_UNARMED; - resume_info->lara_hitpoints = LARA_MAX_HITPOINTS; -} - -static void M_RestoreResumeInfo(M_PRIV *const p) -{ - RESUME_INFO *const resume_info = GF_GetResumeInfo(p->level); - *resume_info = p->old_resume_info; -} - static bool M_ProcessInput(M_PRIV *const p) { union { @@ -160,9 +129,10 @@ bool Demo_Start(const int32_t level_num) { M_PRIV *const p = &m_Priv; p->level = GF_GetLevel(GFLT_DEMOS, level_num); + ASSERT(p->level != nullptr); + ASSERT(GF_GetCurrentLevel() == p->level); M_PrepareConfig(p); - M_PrepareResumeInfo(p); // Remember old inputs in case the demo was forcefully started with some // keys pressed. In that case, it should only be stopped if the user @@ -171,12 +141,6 @@ bool Demo_Start(const int32_t level_num) Interpolation_Remember(); - Random_SeedDraw(0xD371F947); - Random_SeedControl(0xD371F947); - - if (!Level_Initialise(p->level)) { - return false; - } if (g_DemoData == nullptr) { LOG_ERROR("Level '%s' has no demo data", p->level->path); return false; @@ -224,7 +188,6 @@ void Demo_End(void) { M_PRIV *const p = &m_Priv; M_RestoreConfig(p); - M_RestoreResumeInfo(p); Text_Remove(p->text); p->text = nullptr; g_GameInfo.showing_demo = false; @@ -234,7 +197,6 @@ void Demo_Pause(void) { M_PRIV *const p = &m_Priv; M_RestoreConfig(p); - M_RestoreResumeInfo(p); Text_Hide(p->text, true); g_GameInfo.showing_demo = false; } @@ -243,7 +205,6 @@ void Demo_Unpause(void) { M_PRIV *const p = &m_Priv; M_PrepareConfig(p); - M_PrepareResumeInfo(p); Text_Hide(p->text, false); g_GameInfo.showing_demo = true; } @@ -283,7 +244,7 @@ GF_COMMAND Demo_Control(void) if (g_LevelComplete || g_InputDB.menu_confirm || g_InputDB.menu_back) { return (GF_COMMAND) { - .action = GF_LEVEL_COMPLETE, + .action = GF_EXIT_TO_TITLE, .param = p->level->num, }; } @@ -292,7 +253,7 @@ GF_COMMAND Demo_Control(void) if (!M_ProcessInput(p)) { return (GF_COMMAND) { - .action = GF_LEVEL_COMPLETE, + .action = GF_EXIT_TO_TITLE, .param = p->level->num, }; } diff --git a/src/tr1/game/game/game.c b/src/tr1/game/game/game.c index a2dcd5e8b..5ad09fd93 100644 --- a/src/tr1/game/game/game.c +++ b/src/tr1/game/game/game.c @@ -69,82 +69,6 @@ void Game_ProcessInput(void) } } -GF_COMMAND Game_Stop_Legacy(void) -{ - Sound_StopAll(); - Music_Stop(); - const GF_LEVEL *const current_level = Game_GetCurrentLevel(); - const GF_LEVEL *const next_level = GF_GetLevelAfter(current_level); - Savegame_PersistGameToCurrentInfo(current_level); - - if (current_level == GF_GetLastLevel()) { - g_Config.profile.new_game_plus_unlock = true; - Config_Write(); - g_GameInfo.bonus_level_unlock = - Stats_CheckAllSecretsCollected(GFL_NORMAL); - } - - // play specific level - if (g_LevelComplete && g_GameInfo.select_level_num != -1) { - if (current_level != nullptr) { - Savegame_CarryCurrentInfoToNextLevel( - current_level, - GF_GetLevel(GFLT_MAIN, g_GameInfo.select_level_num)); - } - return (GF_COMMAND) { - .action = GF_SELECT_GAME, - .param = g_GameInfo.select_level_num, - }; - } - - // carry info to the next level - if (next_level != nullptr) { - // TODO: this should be moved to GFS_LEVEL_COMPLETE handler, probably - Savegame_CarryCurrentInfoToNextLevel(current_level, next_level); - Savegame_ApplyLogicToCurrentInfo(next_level); - } - - // normal level completion - if (g_LevelComplete) { - // TODO: why is this made unavailable? - GF_GetResumeInfo(current_level)->flags.available = 0; - return (GF_COMMAND) { - .action = GF_LEVEL_COMPLETE, - .param = g_GameInfo.select_level_num, - }; - } - - if (g_GameInfo.passport_selection == PASSPORT_MODE_LOAD_GAME) { - return (GF_COMMAND) { - .action = GF_START_SAVED_GAME, - .param = g_GameInfo.select_save_slot, - }; - } else if (g_GameInfo.passport_selection == PASSPORT_MODE_SELECT_LEVEL) { - return (GF_COMMAND) { - .action = GF_SELECT_GAME, - .param = g_GameInfo.select_level_num, - }; - } else if (g_GameInfo.passport_selection == PASSPORT_MODE_STORY_SO_FAR) { - return (GF_COMMAND) { - .action = GF_STORY_SO_FAR, - .param = g_GameInfo.select_save_slot, - }; - } else if (g_GameInfo.passport_selection == PASSPORT_MODE_RESTART) { - return (GF_COMMAND) { - .action = GF_RESTART_GAME, - .param = current_level->num, - }; - } else if (g_GameInfo.passport_selection == PASSPORT_MODE_NEW_GAME) { - Savegame_InitCurrentInfo(); - return (GF_COMMAND) { - .action = GF_START_GAME, - .param = GF_GetFirstLevel()->num, - }; - } else { - return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; - } -} - bool Game_Start(const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) { Game_SetCurrentLevel(level); @@ -162,6 +86,7 @@ bool Game_Start(const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) void Game_End(void) { + Savegame_PersistGameToCurrentInfo(Game_GetCurrentLevel()); } void Game_Suspend(void) @@ -190,7 +115,9 @@ GF_COMMAND Game_Control(const bool demo_mode) Lara_Cheat_Control(); if (g_LevelComplete) { - return Game_Stop_Legacy(); + Sound_StopAll(); + Music_Stop(); + return (GF_COMMAND) { .action = GF_LEVEL_COMPLETE }; } Input_Update(); diff --git a/src/tr1/game/game_flow/common.c b/src/tr1/game/game_flow/common.c deleted file mode 100644 index d246e5c42..000000000 --- a/src/tr1/game/game_flow/common.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "game/game_flow/common.h" - -#include "global/vars.h" - -#include -#include -#include -#include - -RESUME_INFO *GF_GetResumeInfo(const GF_LEVEL *const level) -{ - ASSERT(level != nullptr); - if (GF_GetLevelTableType(level->type) == GFLT_MAIN) { - return &g_GameInfo.current[level->num]; - } else if (level->type == GFL_DEMO) { - return &g_GameInfo.current[0]; - } - LOG_WARNING( - "Warning: unable to get resume info for level %d (type=%s)", level->num, - ENUM_MAP_TO_STRING(GF_LEVEL_TYPE, level->type)); - return nullptr; -} diff --git a/src/tr1/game/game_flow/common.h b/src/tr1/game/game_flow/common.h index 2f3d4d2ca..a1ca55061 100644 --- a/src/tr1/game/game_flow/common.h +++ b/src/tr1/game/game_flow/common.h @@ -1,7 +1,3 @@ #pragma once -#include "global/types.h" - #include - -RESUME_INFO *GF_GetResumeInfo(const GF_LEVEL *level); diff --git a/src/tr1/game/game_flow/sequencer.c b/src/tr1/game/game_flow/sequencer.c index ec03b0c32..5e550ce61 100644 --- a/src/tr1/game/game_flow/sequencer.c +++ b/src/tr1/game/game_flow/sequencer.c @@ -14,8 +14,8 @@ #include "global/vars.h" #include +#include #include -#include #define DECLARE_EVENT_HANDLER(name) \ GF_COMMAND name( \ @@ -25,13 +25,13 @@ static DECLARE_EVENT_HANDLER(M_HandleExitToTitle); static DECLARE_EVENT_HANDLER(M_HandleLoadLevel); static DECLARE_EVENT_HANDLER(M_HandlePlayLevel); -static DECLARE_EVENT_HANDLER(M_HandleLevelStats); -static DECLARE_EVENT_HANDLER(M_HandleTotalStats); -static DECLARE_EVENT_HANDLER(M_HandlePicture); -static DECLARE_EVENT_HANDLER(M_HandleLevelComplete); static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene); static DECLARE_EVENT_HANDLER(M_HandlePlayFMV); static DECLARE_EVENT_HANDLER(M_HandlePlayMusic); +static DECLARE_EVENT_HANDLER(M_HandlePicture); +static DECLARE_EVENT_HANDLER(M_HandleLevelComplete); +static DECLARE_EVENT_HANDLER(M_HandleLevelStats); +static DECLARE_EVENT_HANDLER(M_HandleTotalStats); static DECLARE_EVENT_HANDLER(M_HandleSetCameraAngle); static DECLARE_EVENT_HANDLER(M_HandleFlipMap); static DECLARE_EVENT_HANDLER(M_HandleAddItem); @@ -47,14 +47,14 @@ static DECLARE_EVENT_HANDLER((*m_EventHandlers[GFS_NUMBER_OF])) = { [GFS_EXIT_TO_TITLE] = M_HandleExitToTitle, [GFS_LOAD_LEVEL] = M_HandleLoadLevel, [GFS_PLAY_LEVEL] = M_HandlePlayLevel, - [GFS_LEVEL_STATS] = M_HandleLevelStats, - [GFS_TOTAL_STATS] = M_HandleTotalStats, - [GFS_LOADING_SCREEN] = M_HandlePicture, - [GFS_DISPLAY_PICTURE] = M_HandlePicture, - [GFS_LEVEL_COMPLETE] = M_HandleLevelComplete, [GFS_PLAY_CUTSCENE] = M_HandlePlayCutscene, [GFS_PLAY_FMV] = M_HandlePlayFMV, [GFS_PLAY_MUSIC] = M_HandlePlayMusic, + [GFS_LOADING_SCREEN] = M_HandlePicture, + [GFS_DISPLAY_PICTURE] = M_HandlePicture, + [GFS_LEVEL_COMPLETE] = M_HandleLevelComplete, + [GFS_LEVEL_STATS] = M_HandleLevelStats, + [GFS_TOTAL_STATS] = M_HandleTotalStats, [GFS_SET_CAMERA_ANGLE] = M_HandleSetCameraAngle, [GFS_FLIP_MAP] = M_HandleFlipMap, [GFS_ADD_ITEM] = M_HandleAddItem, @@ -177,7 +177,7 @@ static DECLARE_EVENT_HANDLER(M_HandleLoadLevel) } Stats_CalculateStats(); - RESUME_INFO *const resume = GF_GetResumeInfo(level); + RESUME_INFO *const resume = Savegame_GetCurrentInfo(level); if (resume != nullptr) { resume->stats.max_pickup_count = Stats_GetPickups(); resume->stats.max_kill_count = Stats_GetKillables(); @@ -194,66 +194,62 @@ static DECLARE_EVENT_HANDLER(M_HandleLoadLevel) static DECLARE_EVENT_HANDLER(M_HandlePlayLevel) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; - if (level->type == GFL_CUTSCENE) { - if (seq_ctx != GFSC_SAVED) { - gf_cmd = GF_RunCutscene((int32_t)(intptr_t)event->data); - if (gf_cmd.action == GF_LEVEL_COMPLETE) { - gf_cmd.action = GF_NOOP; - } - } - } else if (seq_ctx == GFSC_STORY) { + if (seq_ctx == GFSC_STORY) { const int32_t savegame_level_num = (int32_t)(intptr_t)seq_ctx_arg; if (savegame_level_num == level->num) { return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } } else if (level->type == GFL_DEMO) { - return GF_RunDemo(level->num); + ASSERT(GF_GetCurrentLevel() == level); + gf_cmd = GF_RunDemo(level->num); + } else if (level->type == GFL_CUTSCENE) { + ASSERT(GF_GetCurrentLevel() == level); + gf_cmd = GF_RunCutscene(level->num); } else { if (seq_ctx != GFSC_SAVED && level != GF_GetFirstLevel()) { Lara_RevertToPistolsIfNeeded(); } gf_cmd = GF_RunGame(level, seq_ctx); - if (gf_cmd.action == GF_LEVEL_COMPLETE) { - gf_cmd.action = GF_NOOP; - } + } + if (gf_cmd.action == GF_LEVEL_COMPLETE) { + gf_cmd.action = GF_NOOP; } return gf_cmd; } -static DECLARE_EVENT_HANDLER(M_HandleLevelStats) +static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; - if (seq_ctx == GFSC_NORMAL) { - const GF_LEVEL *const current_level = Game_GetCurrentLevel(); - PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) { - .background_type = BK_TRANSPARENT, - .level_num = current_level->num, - .show_final_stats = false, - .use_bare_style = true, - }); - gf_cmd = PhaseExecutor_Run(phase); - Phase_Stats_Destroy(phase); + const int16_t cutscene_num = (int16_t)(intptr_t)event->data; + if (seq_ctx != GFSC_SAVED) { + gf_cmd = GF_DoCutsceneSequence(cutscene_num); + if (gf_cmd.action == GF_LEVEL_COMPLETE) { + gf_cmd.action = GF_NOOP; + } } return gf_cmd; } -static DECLARE_EVENT_HANDLER(M_HandleTotalStats) +static DECLARE_EVENT_HANDLER(M_HandlePlayFMV) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; - if (seq_ctx == GFSC_NORMAL && g_Config.gameplay.enable_total_stats) { - PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) { - .background_type = BK_IMAGE, - .background_path = event->data, - .level_num = level->num, - .show_final_stats = true, - .use_bare_style = false, - }); - gf_cmd = PhaseExecutor_Run(phase); - Phase_Stats_Destroy(phase); + const int16_t fmv_id = (int16_t)(intptr_t)event->data; + if (seq_ctx != GFSC_SAVED) { + if (fmv_id < 0 || fmv_id >= g_GameFlow.fmv_count) { + LOG_ERROR("Invalid FMV number: %d", fmv_id); + } else { + FMV_Play(g_GameFlow.fmvs[fmv_id].path); + } } return gf_cmd; } +static DECLARE_EVENT_HANDLER(M_HandlePlayMusic) +{ + Music_Play((int32_t)(intptr_t)event->data); + return (GF_COMMAND) { .action = GF_NOOP }; +} + static DECLARE_EVENT_HANDLER(M_HandlePicture) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; @@ -289,9 +285,36 @@ static DECLARE_EVENT_HANDLER(M_HandleLevelComplete) } const GF_LEVEL *const current_level = Game_GetCurrentLevel(); const GF_LEVEL *const next_level = GF_GetLevelAfter(current_level); + + if (current_level == GF_GetLastLevel()) { + g_Config.profile.new_game_plus_unlock = true; + Config_Write(); + g_GameInfo.bonus_level_unlock = + Stats_CheckAllSecretsCollected(GFL_NORMAL); + } + + // play specific level + if (g_GameInfo.select_level_num != -1) { + const GF_LEVEL *const select_level = + GF_GetLevel(GFLT_MAIN, g_GameInfo.select_level_num); + if (current_level != nullptr && select_level != nullptr) { + Savegame_CarryCurrentInfoToNextLevel(current_level, select_level); + } + return (GF_COMMAND) { + .action = GF_SELECT_GAME, + .param = g_GameInfo.select_level_num, + }; + } + + // missing level if (next_level == nullptr) { return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } + + // carry info to the next level + Savegame_CarryCurrentInfoToNextLevel(current_level, next_level); + Savegame_ApplyLogicToCurrentInfo(next_level); + if (next_level->type == GFL_BONUS && !g_GameInfo.bonus_level_unlock) { return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } @@ -301,37 +324,37 @@ static DECLARE_EVENT_HANDLER(M_HandleLevelComplete) }; } -static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene) +static DECLARE_EVENT_HANDLER(M_HandleLevelStats) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; - Music_Stop(); - const int16_t cutscene_num = (int16_t)(intptr_t)event->data; - if (seq_ctx != GFSC_SAVED) { - gf_cmd = GF_DoCutsceneSequence(cutscene_num); - if (gf_cmd.action == GF_LEVEL_COMPLETE) { - gf_cmd.action = GF_NOOP; - } + if (seq_ctx == GFSC_NORMAL) { + const GF_LEVEL *const current_level = Game_GetCurrentLevel(); + PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) { + .background_type = BK_TRANSPARENT, + .level_num = current_level->num, + .show_final_stats = false, + .use_bare_style = true, + }); + gf_cmd = PhaseExecutor_Run(phase); + Phase_Stats_Destroy(phase); } return gf_cmd; } -static DECLARE_EVENT_HANDLER(M_HandlePlayFMV) +static DECLARE_EVENT_HANDLER(M_HandleTotalStats) { - const int16_t fmv_id = (int16_t)(intptr_t)event->data; - if (seq_ctx != GFSC_SAVED) { - if (fmv_id < 0 || fmv_id >= g_GameFlow.fmv_count) { - LOG_ERROR("Invalid FMV number: %d", fmv_id); - } else { - FMV_Play(g_GameFlow.fmvs[fmv_id].path); - } + GF_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE }; + if (seq_ctx == GFSC_NORMAL && g_Config.gameplay.enable_total_stats) { + PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) { + .background_type = BK_IMAGE, + .background_path = event->data, + .show_final_stats = true, + .use_bare_style = false, + }); + gf_cmd = PhaseExecutor_Run(phase); + Phase_Stats_Destroy(phase); } - return (GF_COMMAND) { .action = GF_NOOP }; -} - -static DECLARE_EVENT_HANDLER(M_HandlePlayMusic) -{ - Music_Play((int32_t)(intptr_t)event->data); - return (GF_COMMAND) { .action = GF_NOOP }; + return gf_cmd; } static DECLARE_EVENT_HANDLER(M_HandleSetCameraAngle) @@ -369,19 +392,19 @@ static DECLARE_EVENT_HANDLER(M_HandleRemoveWeapons) return (GF_COMMAND) { .action = GF_NOOP }; } -static DECLARE_EVENT_HANDLER(M_HandleRemoveScions) +static DECLARE_EVENT_HANDLER(M_HandleRemoveAmmo) { - if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED) { - g_GameInfo.remove_scions = true; + if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED + && !(g_GameInfo.bonus_flag & GBF_NGPLUS)) { + g_GameInfo.remove_ammo = true; } return (GF_COMMAND) { .action = GF_NOOP }; } -static DECLARE_EVENT_HANDLER(M_HandleRemoveAmmo) +static DECLARE_EVENT_HANDLER(M_HandleRemoveScions) { - if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED - && !(g_GameInfo.bonus_flag & GBF_NGPLUS)) { - g_GameInfo.remove_ammo = true; + if (seq_ctx != GFSC_STORY && seq_ctx != GFSC_SAVED) { + g_GameInfo.remove_scions = true; } return (GF_COMMAND) { .action = GF_NOOP }; } diff --git a/src/tr1/game/game_flow/sequencer.h b/src/tr1/game/game_flow/sequencer.h index 4b5de20a4..b28b197e6 100644 --- a/src/tr1/game/game_flow/sequencer.h +++ b/src/tr1/game/game_flow/sequencer.h @@ -2,4 +2,6 @@ #include +GF_COMMAND GF_DoLevelSequence( + const GF_LEVEL *start_level, GF_SEQUENCE_CONTEXT seq_ctx); GF_COMMAND GF_PlayAvailableStory(int32_t slot_num); diff --git a/src/tr1/game/game_flow/sequencer_misc.c b/src/tr1/game/game_flow/sequencer_misc.c index ed0cd3148..30186184c 100644 --- a/src/tr1/game/game_flow/sequencer_misc.c +++ b/src/tr1/game/game_flow/sequencer_misc.c @@ -1,4 +1,5 @@ #include "game/demo.h" +#include "game/game.h" #include "game/game_flow/common.h" #include "game/game_flow/sequencer.h" #include "game/game_flow/vars.h" @@ -8,7 +9,7 @@ #include #include -GF_COMMAND GF_TitleSequence(void) +GF_COMMAND GF_RunTitle(void) { Savegame_UnbindSlot(); GameStringTable_Apply(nullptr); @@ -37,3 +38,27 @@ GF_COMMAND GF_PlayAvailableStory(const int32_t slot_num) } return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } + +GF_COMMAND GF_DoLevelSequence( + const GF_LEVEL *const start_level, const GF_SEQUENCE_CONTEXT seq_ctx) +{ + const GF_LEVEL *current_level = start_level; + const GF_LEVEL_TABLE_TYPE level_table_type = + GF_GetLevelTableType(current_level->type); + const int32_t level_count = GF_GetLevelTable(level_table_type)->count; + while (true) { + const GF_COMMAND gf_cmd = + GF_InterpretSequence(current_level, seq_ctx, nullptr); + + if (gf_cmd.action != GF_NOOP && gf_cmd.action != GF_LEVEL_COMPLETE) { + return gf_cmd; + } + if (Game_IsInGym()) { + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; + } + if (current_level->num + 1 >= level_count) { + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; + } + current_level++; + } +} diff --git a/src/tr1/game/gun/gun_misc.c b/src/tr1/game/gun/gun_misc.c index bea0a76a6..85dac1f13 100644 --- a/src/tr1/game/gun/gun_misc.c +++ b/src/tr1/game/gun/gun_misc.c @@ -2,12 +2,12 @@ #include "game/collide.h" #include "game/game.h" -#include "game/game_flow.h" #include "game/input.h" #include "game/inventory.h" #include "game/items.h" #include "game/los.h" #include "game/random.h" +#include "game/savegame.h" #include "game/sound.h" #include "game/spawn.h" #include "global/const.h" @@ -490,7 +490,7 @@ int32_t Gun_FireWeapon( void Gun_HitTarget(ITEM *item, GAME_VECTOR *hitpos, int16_t damage) { if (item->hit_points > 0 && item->hit_points <= damage) { - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.kill_count++; + Savegame_GetCurrentInfo(Game_GetCurrentLevel())->stats.kill_count++; if (g_Config.gameplay.target_mode == TLM_SEMI) { g_Lara.target = nullptr; } diff --git a/src/tr1/game/lara/common.c b/src/tr1/game/lara/common.c index fc6cbcb6d..4aed6e283 100644 --- a/src/tr1/game/lara/common.c +++ b/src/tr1/game/lara/common.c @@ -469,7 +469,7 @@ void Lara_InitialiseLoad(int16_t item_num) void Lara_Initialise(const GF_LEVEL *const level) { - RESUME_INFO *const resume = GF_GetResumeInfo(level); + RESUME_INFO *const resume = Savegame_GetCurrentInfo(level); g_LaraItem->collidable = 0; g_LaraItem->data = &g_Lara; @@ -532,7 +532,7 @@ void Lara_InitialiseInventory(const GF_LEVEL *const level) { Inv_RemoveAllItems(); - RESUME_INFO *const resume = GF_GetResumeInfo(level); + RESUME_INFO *const resume = Savegame_GetCurrentInfo(level); g_Lara.pistols.ammo = 1000; @@ -651,7 +651,7 @@ void Lara_RevertToPistolsIfNeeded(void) void Lara_InitialiseMeshes(const GF_LEVEL *const level) { - const RESUME_INFO *const resume = GF_GetResumeInfo(level); + const RESUME_INFO *const resume = Savegame_GetCurrentInfo(level); if (resume != nullptr && resume->flags.costume) { for (LARA_MESH mesh = LM_FIRST; mesh < LM_NUMBER_OF; mesh++) { diff --git a/src/tr1/game/level.c b/src/tr1/game/level.c index 95b2232b0..680800701 100644 --- a/src/tr1/game/level.c +++ b/src/tr1/game/level.c @@ -17,7 +17,9 @@ #include "game/objects/setup.h" #include "game/output.h" #include "game/overlay.h" +#include "game/random.h" #include "game/room.h" +#include "game/savegame.h" #include "game/shell.h" #include "game/sound.h" #include "game/stats.h" @@ -924,10 +926,14 @@ bool Level_Initialise(const GF_LEVEL *const level) { BENCHMARK *const benchmark = Benchmark_Start(); LOG_DEBUG("num=%d (%s)", level->num, level->path); + if (level->type == GFL_DEMO) { + Random_SeedDraw(0xD371F947); + Random_SeedControl(0xD371F947); + } g_GameInfo.select_level_num = -1; - const int32_t level_num = level->num; - RESUME_INFO *const resume = GF_GetResumeInfo(level); + + RESUME_INFO *const resume = Savegame_GetCurrentInfo(level); if (resume != nullptr) { resume->stats.timer = 0; resume->stats.secret_flags = 0; diff --git a/src/tr1/game/objects/creatures/mummy.c b/src/tr1/game/objects/creatures/mummy.c index f1b7d33ca..d0ed47375 100644 --- a/src/tr1/game/objects/creatures/mummy.c +++ b/src/tr1/game/objects/creatures/mummy.c @@ -3,9 +3,9 @@ #include "game/carrier.h" #include "game/creature.h" #include "game/game.h" -#include "game/game_flow.h" #include "game/items.h" #include "game/objects/common.h" +#include "game/savegame.h" #include "global/const.h" #include "global/vars.h" @@ -69,7 +69,7 @@ void Mummy_Control(int16_t item_num) if (item->status == IS_DEACTIVATED) { // Count kill if Lara touches mummy and it falls. if (item->hit_points > 0) { - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.kill_count++; + Savegame_GetCurrentInfo(Game_GetCurrentLevel())->stats.kill_count++; } Item_RemoveActive(item_num); if (item->hit_points != DONT_TARGET) { diff --git a/src/tr1/game/objects/general/pickup.c b/src/tr1/game/objects/general/pickup.c index 6260921fd..a61096242 100644 --- a/src/tr1/game/objects/general/pickup.c +++ b/src/tr1/game/objects/general/pickup.c @@ -2,7 +2,6 @@ #include "game/effects.h" #include "game/game.h" -#include "game/game_flow.h" #include "game/gun.h" #include "game/input.h" #include "game/inventory.h" @@ -11,6 +10,7 @@ #include "game/objects/common.h" #include "game/overlay.h" #include "game/random.h" +#include "game/savegame.h" #include "global/const.h" #include "global/vars.h" @@ -143,7 +143,7 @@ static void M_GetItem(int16_t item_num, ITEM *item, ITEM *lara_item) item->status = IS_INVISIBLE; Item_RemoveDrawn(item_num); Item_RemoveActive(item_num); - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.pickup_count++; + Savegame_GetCurrentInfo(Game_GetCurrentLevel())->stats.pickup_count++; g_Lara.interact_target.is_moving = false; } diff --git a/src/tr1/game/objects/general/scion1.c b/src/tr1/game/objects/general/scion1.c index 7f8f19481..544bd835d 100644 --- a/src/tr1/game/objects/general/scion1.c +++ b/src/tr1/game/objects/general/scion1.c @@ -2,13 +2,14 @@ #include "game/camera.h" #include "game/game.h" -#include "game/game_flow.h" #include "game/input.h" #include "game/inventory.h" #include "game/items.h" #include "game/lara/common.h" +#include "game/level.h" #include "game/objects/common.h" #include "game/overlay.h" +#include "game/savegame.h" #include "global/vars.h" #define EXTRA_ANIM_PEDESTAL_SCION 0 @@ -63,7 +64,8 @@ void Scion1_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll) Inv_AddItem(item->object_id); item->status = IS_INVISIBLE; Item_RemoveDrawn(item_num); - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.pickup_count++; + Savegame_GetCurrentInfo(Game_GetCurrentLevel()) + ->stats.pickup_count++; } } else if ( g_Input.action && g_Lara.gun_status == LGS_ARMLESS diff --git a/src/tr1/game/room.c b/src/tr1/game/room.c index 06913d7f7..77faf5e4b 100644 --- a/src/tr1/game/room.c +++ b/src/tr1/game/room.c @@ -2,7 +2,6 @@ #include "game/camera.h" #include "game/game.h" -#include "game/game_flow.h" #include "game/items.h" #include "game/lara/misc.h" #include "game/lot.h" @@ -11,6 +10,7 @@ #include "game/objects/general/keyhole.h" #include "game/objects/general/pickup.h" #include "game/objects/general/switch.h" +#include "game/savegame.h" #include "game/shell.h" #include "game/sound.h" #include "global/const.h" @@ -880,7 +880,8 @@ void Room_TestSectorTrigger(const ITEM *const item, const SECTOR *const sector) case TO_SECRET: { const int16_t secret_num = 1 << (int16_t)(intptr_t)cmd->parameter; - RESUME_INFO *resume_info = GF_GetResumeInfo(Game_GetCurrentLevel()); + RESUME_INFO *resume_info = + Savegame_GetCurrentInfo(Game_GetCurrentLevel()); if (resume_info->stats.secret_flags & secret_num) { break; } diff --git a/src/tr1/game/savegame.h b/src/tr1/game/savegame.h index 3d3ce48f2..af1aa587b 100644 --- a/src/tr1/game/savegame.h +++ b/src/tr1/game/savegame.h @@ -63,6 +63,8 @@ void Savegame_ScanAvailableLevels(REQUEST_INFO *req); void Savegame_HighlightNewestSlot(void); bool Savegame_RestartAvailable(int32_t slot_num); +RESUME_INFO *Savegame_GetCurrentInfo(const GF_LEVEL *level); + void Savegame_ApplyLogicToCurrentInfo(const GF_LEVEL *level); void Savegame_ResetCurrentInfo(const GF_LEVEL *level); void Savegame_CarryCurrentInfoToNextLevel( diff --git a/src/tr1/game/savegame/savegame.c b/src/tr1/game/savegame/savegame.c index 51bca1791..d10f95eb1 100644 --- a/src/tr1/game/savegame/savegame.c +++ b/src/tr1/game/savegame/savegame.c @@ -23,6 +23,7 @@ #include #include +#include #include #include @@ -175,10 +176,50 @@ static void M_LoadPostprocess(void) void Savegame_Init(void) { - g_GameInfo.current = - Memory_Alloc(sizeof(RESUME_INFO) * GF_GetLevelTable(GFLT_MAIN)->count); + g_GameInfo.current = Memory_Alloc( + sizeof(RESUME_INFO) + * (GF_GetLevelTable(GFLT_MAIN)->count + + (GF_GetLevelTable(GFLT_DEMOS)->count >= 0 ? 1 : 0))); m_SaveSlots = g_Config.gameplay.maximum_save_slots; m_SavegameInfo = Memory_Alloc(sizeof(SAVEGAME_INFO) * m_SaveSlots); + + const GF_LEVEL_TABLE *const level_table = GF_GetLevelTable(GFLT_DEMOS); + for (int32_t i = 0; i < level_table->count; i++) { + RESUME_INFO *const resume_info = + Savegame_GetCurrentInfo(&level_table->levels[i]); + resume_info->flags.available = 1; + resume_info->flags.costume = 0; + resume_info->num_medis = 0; + resume_info->num_big_medis = 0; + resume_info->num_scions = 0; + resume_info->flags.got_pistols = 1; + resume_info->flags.got_shotgun = 0; + resume_info->flags.got_magnums = 0; + resume_info->flags.got_uzis = 0; + resume_info->pistol_ammo = 1000; + resume_info->shotgun_ammo = 0; + resume_info->magnum_ammo = 0; + resume_info->uzi_ammo = 0; + resume_info->gun_status = LGS_ARMLESS; + resume_info->equipped_gun_type = LGT_PISTOLS; + resume_info->holsters_gun_type = LGT_PISTOLS; + resume_info->back_gun_type = LGT_UNARMED; + resume_info->lara_hitpoints = LARA_MAX_HITPOINTS; + } +} + +RESUME_INFO *Savegame_GetCurrentInfo(const GF_LEVEL *const level) +{ + ASSERT(level != nullptr); + if (GF_GetLevelTableType(level->type) == GFLT_MAIN) { + return &g_GameInfo.current[level->num]; + } else if (level->type == GFL_DEMO) { + return &g_GameInfo.current[GF_GetLevelTable(GFLT_MAIN)->count]; + } + LOG_WARNING( + "Warning: unable to get resume info for level %d (type=%s)", level->num, + ENUM_MAP_TO_STRING(GF_LEVEL_TYPE, level->type)); + return nullptr; } void Savegame_Shutdown(void) @@ -232,19 +273,19 @@ void Savegame_InitCurrentInfo(void) const GF_LEVEL *const level = &level_table->levels[i]; Savegame_ResetCurrentInfo(level); Savegame_ApplyLogicToCurrentInfo(level); - GF_GetResumeInfo(level)->flags.available = 0; + Savegame_GetCurrentInfo(level)->flags.available = 0; } if (GF_GetGymLevel() != nullptr) { - GF_GetResumeInfo(GF_GetGymLevel())->flags.available = 1; + Savegame_GetCurrentInfo(GF_GetGymLevel())->flags.available = 1; } if (GF_GetFirstLevel() != nullptr) { - GF_GetResumeInfo(GF_GetFirstLevel())->flags.available = 1; + Savegame_GetCurrentInfo(GF_GetFirstLevel())->flags.available = 1; } } void Savegame_ApplyLogicToCurrentInfo(const GF_LEVEL *const level) { - RESUME_INFO *const current = GF_GetResumeInfo(level); + RESUME_INFO *const current = Savegame_GetCurrentInfo(level); LOG_INFO("Applying game logic to level #%d", level->num); if (!g_Config.gameplay.disable_healing_between_levels @@ -342,7 +383,7 @@ void Savegame_ApplyLogicToCurrentInfo(const GF_LEVEL *const level) void Savegame_ResetCurrentInfo(const GF_LEVEL *const level) { LOG_INFO("Resetting resume info for level #%d", level->num); - RESUME_INFO *const current = GF_GetResumeInfo(level); + RESUME_INFO *const current = Savegame_GetCurrentInfo(level); memset(current, 0, sizeof(RESUME_INFO)); } @@ -352,15 +393,15 @@ void Savegame_CarryCurrentInfoToNextLevel( LOG_INFO( "Copying resume info from level #%d to level #%d", src_level->num, dst_level->num); - RESUME_INFO *const src_resume = GF_GetResumeInfo(src_level); - RESUME_INFO *const dst_resume = GF_GetResumeInfo(dst_level); + RESUME_INFO *const src_resume = Savegame_GetCurrentInfo(src_level); + RESUME_INFO *const dst_resume = Savegame_GetCurrentInfo(dst_level); memcpy(dst_resume, src_resume, sizeof(RESUME_INFO)); } void Savegame_PersistGameToCurrentInfo(const GF_LEVEL *const level) { ASSERT(level != nullptr); - RESUME_INFO *current = GF_GetResumeInfo(level); + RESUME_INFO *current = Savegame_GetCurrentInfo(level); current->lara_hitpoints = g_LaraItem->hit_points; current->flags.available = 1; diff --git a/src/tr1/game/savegame/savegame_legacy.c b/src/tr1/game/savegame/savegame_legacy.c index 44b5e39a8..5e7f1a313 100644 --- a/src/tr1/game/savegame/savegame_legacy.c +++ b/src/tr1/game/savegame/savegame_legacy.c @@ -437,7 +437,7 @@ static void M_ReadResumeInfo(MYFILE *fp, GAME_INFO *game_info) const GF_LEVEL_TABLE *const level_table = GF_GetLevelTable(GFLT_MAIN); for (int i = 0; i < level_table->count; i++) { const GF_LEVEL *const level = &level_table->levels[i]; - RESUME_INFO *current = GF_GetResumeInfo(level); + RESUME_INFO *current = Savegame_GetCurrentInfo(level); M_Read(¤t->pistol_ammo, sizeof(uint16_t)); M_Read(¤t->magnum_ammo, sizeof(uint16_t)); M_Read(¤t->uzi_ammo, sizeof(uint16_t)); @@ -472,7 +472,8 @@ static void M_ReadResumeInfo(MYFILE *fp, GAME_INFO *game_info) M_Read(&temp_secret_flags, sizeof(uint16_t)); M_Read(¤t_level, sizeof(uint16_t)); M_SetCurrentPosition(current_level); - RESUME_INFO *const resume_info = GF_GetResumeInfo(Game_GetCurrentLevel()); + RESUME_INFO *const resume_info = + Savegame_GetCurrentInfo(Game_GetCurrentLevel()); resume_info->stats.timer = temp_timer; resume_info->stats.kill_count = temp_kill_count; resume_info->stats.secret_flags = temp_secret_flags; diff --git a/src/tr1/game/shell.c b/src/tr1/game/shell.c index eaef286e9..1aa7892da 100644 --- a/src/tr1/game/shell.c +++ b/src/tr1/game/shell.c @@ -202,14 +202,14 @@ void Shell_Main(void) const int32_t level_num = gf_cmd.param; const GF_LEVEL *const level = GF_GetLevel(GFLT_MAIN, level_num); if (level != nullptr) { - gf_cmd = GF_InterpretSequence(level, GFSC_NORMAL, nullptr); + gf_cmd = GF_DoLevelSequence(level, GFSC_NORMAL); } break; } case GF_SELECT_GAME: { const GF_LEVEL *const level = GF_GetLevel(GFLT_MAIN, gf_cmd.param); - gf_cmd = GF_InterpretSequence(level, GFSC_SELECT, nullptr); + gf_cmd = GF_DoLevelSequence(level, GFSC_NORMAL); break; } @@ -222,7 +222,7 @@ void Shell_Main(void) } else { Savegame_BindSlot(slot_num); const GF_LEVEL *const level = GF_GetLevel(GFLT_MAIN, level_num); - gf_cmd = GF_InterpretSequence(level, GFSC_SAVED, nullptr); + gf_cmd = GF_DoLevelSequence(level, GFSC_SAVED); } break; } @@ -253,7 +253,7 @@ void Shell_Main(void) if (g_GameFlow.title_level == nullptr) { Shell_ExitSystem("Title disabled"); } else { - gf_cmd = GF_TitleSequence(); + gf_cmd = GF_RunTitle(); } break; diff --git a/src/tr1/game/stats/common.c b/src/tr1/game/stats/common.c index 1f456496e..f68b3cb02 100644 --- a/src/tr1/game/stats/common.c +++ b/src/tr1/game/stats/common.c @@ -5,6 +5,7 @@ #include "game/items.h" #include "game/objects/common.h" #include "game/objects/vars.h" +#include "game/savegame.h" #include "game/stats.h" #include "global/const.h" #include "global/types.h" @@ -129,7 +130,7 @@ void Stats_ComputeFinal(GF_LEVEL_TYPE level_type, FINAL_STATS *final_stats) if (level->type != level_type) { continue; } - const LEVEL_STATS *level_stats = &GF_GetResumeInfo(level)->stats; + const LEVEL_STATS *level_stats = &Savegame_GetCurrentInfo(level)->stats; final_stats->kill_count += level_stats->kill_count; final_stats->pickup_count += level_stats->pickup_count; @@ -230,7 +231,7 @@ void Stats_StartTimer(void) { ClockTimer_Sync(&m_StatsTimer.timer); m_StatsTimer.start_timer = - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.timer; + Savegame_GetCurrentInfo(Game_GetCurrentLevel())->stats.timer; } void Stats_UpdateTimer(void) @@ -240,7 +241,7 @@ void Stats_UpdateTimer(void) } const double elapsed = ClockTimer_PeekElapsed(&m_StatsTimer.timer) * LOGIC_FPS; - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.timer = + Savegame_GetCurrentInfo(Game_GetCurrentLevel())->stats.timer = m_StatsTimer.start_timer + elapsed; } #else @@ -253,7 +254,7 @@ void Stats_UpdateTimer(void) if (Game_GetCurrentLevel() == nullptr) { return; } - GF_GetResumeInfo(Game_GetCurrentLevel())->stats.timer++; + Savegame_GetCurrentInfo(Game_GetCurrentLevel())->stats.timer++; } #endif diff --git a/src/tr1/meson.build b/src/tr1/meson.build index 50b73f90e..5b0ec714d 100644 --- a/src/tr1/meson.build +++ b/src/tr1/meson.build @@ -113,7 +113,6 @@ sources = [ 'game/game/game.c', 'game/game/game_draw.c', 'game/game_string.c', - 'game/game_flow/common.c', 'game/game_flow/sequencer.c', 'game/game_flow/sequencer_misc.c', 'game/gun/gun.c', diff --git a/src/tr2/decomp/savegame.c b/src/tr2/decomp/savegame.c index e98ff2ab1..582d3807e 100644 --- a/src/tr2/decomp/savegame.c +++ b/src/tr2/decomp/savegame.c @@ -22,12 +22,12 @@ #include "global/const.h" #include "global/vars.h" +#include #include #include #include -#define MAX_SG_BUFFER_SIZE 6272 #define SAVE_CREATURE (1 << 7) #define SPECIAL_READ_WRITES \ @@ -50,6 +50,7 @@ static void M_Read(void *ptr, size_t size); SPECIAL_READ_WRITES static void M_Skip(size_t size); static void M_ReadStartInfo(MYFILE *fp, START_INFO *start); +static void M_ReadStartInfos(MYFILE *fp); static void M_ReadStats(MYFILE *fp, LEVEL_STATS *const stats); static void M_ReadItems(void); static void M_ReadLara(LARA_INFO *lara); @@ -62,6 +63,7 @@ static void M_Write(const void *ptr, size_t size); #define SPECIAL_READ_WRITE(name, type) static void M_Write##name(type value); SPECIAL_READ_WRITES static void M_WriteStartInfo(MYFILE *fp, const START_INFO *start); +static void M_WriteStartInfos(MYFILE *fp); static void M_WriteStats(MYFILE *fp, const LEVEL_STATS *stats); static void M_WriteItems(void); static void M_WriteLara(const LARA_INFO *lara); @@ -130,6 +132,20 @@ static void M_ReadStartInfo(MYFILE *const fp, START_INFO *const start) M_ReadStats(fp, &start->stats); } +static void M_ReadStartInfos(MYFILE *const fp) +{ + const GF_LEVEL_TABLE *const level_table = GF_GetLevelTable(GFLT_MAIN); + for (int32_t i = 0; i < 24; i++) { + if (i < level_table->count) { + const GF_LEVEL *const level = &level_table->levels[i]; + M_ReadStartInfo(fp, Savegame_GetCurrentInfo(level)); + } else { + START_INFO dummy_resume_info; + M_ReadStartInfo(fp, &dummy_resume_info); + } + } +} + static void M_ReadStats(MYFILE *const fp, LEVEL_STATS *const stats) { stats->timer = File_ReadU32(fp); @@ -413,6 +429,7 @@ static void M_Write(const void *ptr, const size_t size) static void M_WriteStartInfo(MYFILE *const fp, const START_INFO *const start) { + ASSERT(start != nullptr); File_WriteU16(fp, start->pistol_ammo); File_WriteU16(fp, start->magnum_ammo); File_WriteU16(fp, start->uzi_ammo); @@ -444,6 +461,20 @@ static void M_WriteStartInfo(MYFILE *const fp, const START_INFO *const start) M_WriteStats(fp, &start->stats); } +static void M_WriteStartInfos(MYFILE *const fp) +{ + const GF_LEVEL_TABLE *const level_table = GF_GetLevelTable(GFLT_MAIN); + for (int32_t i = 0; i < 24; i++) { + if (i < level_table->count) { + const GF_LEVEL *const level = &level_table->levels[i]; + M_WriteStartInfo(fp, Savegame_GetCurrentInfo(level)); + } else { + const START_INFO null_resume_info = {}; + M_WriteStartInfo(fp, &null_resume_info); + } + } +} + static void M_WriteStats(MYFILE *const fp, const LEVEL_STATS *const stats) { File_WriteU32(fp, stats->timer); @@ -642,7 +673,7 @@ static void M_WriteFlares(void) void Savegame_ResetCurrentInfo(const GF_LEVEL *const level) { - START_INFO *const current = GF_GetResumeInfo(level); + START_INFO *const current = Savegame_GetCurrentInfo(level); memset(current, 0, sizeof(START_INFO)); } @@ -657,21 +688,21 @@ void Savegame_InitCurrentInfo(void) const GF_LEVEL *const level = &level_table->levels[i]; Savegame_ResetCurrentInfo(level); Savegame_ApplyLogicToCurrentInfo(level); - GF_GetResumeInfo(level)->available = 0; + Savegame_GetCurrentInfo(level)->available = 0; } if (GF_GetGymLevel() != nullptr) { - GF_GetResumeInfo(GF_GetGymLevel())->available = 1; + Savegame_GetCurrentInfo(GF_GetGymLevel())->available = 1; } if (GF_GetFirstLevel() != nullptr) { - GF_GetResumeInfo(GF_GetFirstLevel())->available = 1; + Savegame_GetCurrentInfo(GF_GetFirstLevel())->available = 1; } g_SaveGame.bonus_flag = 0; } void Savegame_ApplyLogicToCurrentInfo(const GF_LEVEL *const level) { - START_INFO *start = GF_GetResumeInfo(level); + START_INFO *start = Savegame_GetCurrentInfo(level); start->has_pistols = 1; start->gun_type = LGT_PISTOLS; start->pistol_ammo = 1000; @@ -748,7 +779,7 @@ void Savegame_ApplyLogicToCurrentInfo(const GF_LEVEL *const level) void Savegame_PersistGameToCurrentInfo(const GF_LEVEL *const level) { - START_INFO *const start = GF_GetResumeInfo(level); + START_INFO *const start = Savegame_GetCurrentInfo(level); start->available = 1; @@ -843,7 +874,7 @@ void CreateSaveGameInfo(void) g_SaveGame.num_key[3] = Inv_RequestItem(O_KEY_ITEM_4); ResetSG(); - memset(g_SaveGame.buffer, 0, sizeof(g_SaveGame.buffer)); + memset(g_SaveGame.buffer, 0, MAX_SG_BUFFER_SIZE); M_WriteS32(g_FlipStatus); for (int32_t i = 0; i < MAX_FLIP_MAPS; i++) { @@ -1025,12 +1056,11 @@ int32_t S_SaveGame(const int32_t slot_num) const GF_LEVEL *const current_level = GF_GetLevel(GFLT_MAIN, g_SaveGame.current_level); - sprintf(file_name, "%s", current_level->title); + memset(file_name, 0, 75); + snprintf(file_name, 75, "%s", current_level->title); File_WriteData(fp, file_name, 75); File_WriteS32(fp, g_SaveCounter); - for (int32_t i = 0; i < 24; i++) { - M_WriteStartInfo(fp, &g_SaveGame.start[i]); - } + M_WriteStartInfos(fp); M_WriteStats(fp, &g_SaveGame.current_stats); File_WriteS16(fp, g_SaveGame.current_level); File_WriteU8(fp, g_SaveGame.bonus_flag); @@ -1073,9 +1103,7 @@ int32_t S_LoadGame(const int32_t slot_num) } File_Skip(fp, 75); File_Skip(fp, 4); - for (int32_t i = 0; i < 24; i++) { - M_ReadStartInfo(fp, &g_SaveGame.start[i]); - } + M_ReadStartInfos(fp); M_ReadStats(fp, &g_SaveGame.current_stats); g_SaveGame.current_level = File_ReadS16(fp); g_SaveGame.bonus_flag = File_ReadU8(fp); diff --git a/src/tr2/decomp/savegame.h b/src/tr2/decomp/savegame.h index 9f2d0789a..c0be43576 100644 --- a/src/tr2/decomp/savegame.h +++ b/src/tr2/decomp/savegame.h @@ -7,9 +7,13 @@ #include -void Savegame_ResetCurrentInfo(const GF_LEVEL *level); +void Savegame_Init(void); +void Savegame_Shutdown(void); void Savegame_InitCurrentInfo(void); + +void Savegame_ResetCurrentInfo(const GF_LEVEL *level); +START_INFO *Savegame_GetCurrentInfo(const GF_LEVEL *level); void Savegame_ApplyLogicToCurrentInfo(const GF_LEVEL *level); void Savegame_PersistGameToCurrentInfo(const GF_LEVEL *level); void CreateSaveGameInfo(void); diff --git a/src/tr2/game/cutscene.c b/src/tr2/game/cutscene.c index 0a2e9a45e..8ad326f1c 100644 --- a/src/tr2/game/cutscene.c +++ b/src/tr2/game/cutscene.c @@ -16,7 +16,7 @@ #include "global/vars.h" #include -#include +#include #include static void M_FixAudioDrift(void); @@ -35,9 +35,7 @@ static void M_FixAudioDrift(void) bool Cutscene_Start(const int32_t level_num) { const GF_LEVEL *const level = GF_GetLevel(GFLT_CUTSCENES, level_num); - if (!Level_Initialise(level, GFSC_NORMAL)) { - return false; - } + ASSERT(GF_GetCurrentLevel() == level); Room_InitCinematic(); CutscenePlayer1_Initialise(g_Lara.item_num); diff --git a/src/tr2/game/demo.c b/src/tr2/game/demo.c index f1d373823..e77c927d0 100644 --- a/src/tr2/game/demo.c +++ b/src/tr2/game/demo.c @@ -1,5 +1,6 @@ #include "game/demo.h" +#include "decomp/savegame.h" #include "game/camera.h" #include "game/game.h" #include "game/game_flow.h" @@ -30,7 +31,6 @@ typedef struct { struct { bool bonus_flag; } old_config; - START_INFO old_start; } M_PRIV; static int32_t m_LastDemoNum = 0; @@ -40,8 +40,6 @@ static INPUT_STATE m_OldDemoInputDB = {}; static void M_PrepareConfig(M_PRIV *p); static void M_RestoreConfig(M_PRIV *p); -static void M_PrepareStartInfo(M_PRIV *p); -static void M_RestoreStartInfo(M_PRIV *p); static void M_PrepareConfig(M_PRIV *const p) { @@ -54,23 +52,6 @@ static void M_RestoreConfig(M_PRIV *const p) g_SaveGame.bonus_flag = p->old_config.bonus_flag; } -static void M_PrepareStartInfo(M_PRIV *const p) -{ - START_INFO *const start = GF_GetResumeInfo(p->level); - p->old_start = *start; - start->available = 1; - start->has_pistols = 1; - start->pistol_ammo = 1000; - start->gun_status = LGS_ARMLESS; - start->gun_type = LGT_PISTOLS; -} - -static void M_RestoreStartInfo(M_PRIV *const p) -{ - START_INFO *const start = GF_GetResumeInfo(p->level); - *start = p->old_start; -} - bool Demo_GetInput(void) { M_PRIV *const p = &m_Priv; @@ -142,20 +123,7 @@ bool Demo_Start(const int32_t level_num) M_PRIV *const p = &m_Priv; p->level = GF_GetLevel(GFLT_DEMOS, level_num); ASSERT(p->level != nullptr); - - M_PrepareConfig(p); - M_PrepareStartInfo(p); - - Random_SeedDraw(0xD371F947); - Random_SeedControl(0xD371F947); - - if (!Level_Initialise(p->level, GFSC_NORMAL)) { - return false; - } - if (g_DemoData == nullptr) { - LOG_ERROR("Level '%s' has no demo data", p->level->path); - return false; - } + ASSERT(GF_GetCurrentLevel() == p->level); p->demo_ptr = g_DemoData; @@ -197,7 +165,6 @@ void Demo_End(void) { M_PRIV *const p = &m_Priv; M_RestoreConfig(p); - M_RestoreStartInfo(p); Text_Remove(p->text); Overlay_HideGameInfo(); Sound_StopAll(); @@ -210,14 +177,12 @@ void Demo_Pause(void) { M_PRIV *const p = &m_Priv; M_RestoreConfig(p); - M_RestoreStartInfo(p); } void Demo_Unpause(void) { M_PRIV *const p = &m_Priv; M_PrepareConfig(p); - M_PrepareStartInfo(p); Stats_StartTimer(); } diff --git a/src/tr2/game/game.c b/src/tr2/game/game.c index 55b14abc7..8115f0ccd 100644 --- a/src/tr2/game/game.c +++ b/src/tr2/game/game.c @@ -26,18 +26,6 @@ bool Game_Start(const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) { Game_SetCurrentLevel(level); - GF_SetCurrentLevel(level); - if (seq_ctx != GFSC_SAVED) { - if (level != nullptr) { - Savegame_ApplyLogicToCurrentInfo(level); - } - InitialiseLevelFlags(); - } - if (!Level_Initialise(level, seq_ctx)) { - Game_SetCurrentLevel(nullptr); - GF_SetCurrentLevel(nullptr); - return false; - } g_OverlayStatus = 1; Camera_Initialise(); @@ -69,9 +57,6 @@ GF_COMMAND Game_Control(const bool demo_mode) } if (g_LevelComplete) { - if (g_GameFlow.is_demo_version && g_GameFlow.single_level) { - return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; - } return (GF_COMMAND) { .action = GF_LEVEL_COMPLETE }; } diff --git a/src/tr2/game/game_flow/common.c b/src/tr2/game/game_flow/common.c deleted file mode 100644 index 29e27ed83..000000000 --- a/src/tr2/game/game_flow/common.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "game/game_flow/common.h" - -#include "global/vars.h" - -#include -#include -#include -#include - -START_INFO *GF_GetResumeInfo(const GF_LEVEL *const level) -{ - ASSERT(level != nullptr); - if (GF_GetLevelTableType(level->type) == GFLT_MAIN) { - return &g_SaveGame.start[level->num]; - } else if (level->type == GFL_DEMO) { - return &g_SaveGame.start[0]; - } - LOG_WARNING( - "Warning: unable to get resume info for level %d (type=%s)", level->num, - ENUM_MAP_TO_STRING(GF_LEVEL_TYPE, level->type)); - return nullptr; -} diff --git a/src/tr2/game/game_flow/common.h b/src/tr2/game/game_flow/common.h index a6b7c7381..a1ca55061 100644 --- a/src/tr2/game/game_flow/common.h +++ b/src/tr2/game/game_flow/common.h @@ -1,7 +1,3 @@ #pragma once -#include "global/types.h" - #include - -START_INFO *GF_GetResumeInfo(const GF_LEVEL *level); diff --git a/src/tr2/game/game_flow/inventory.c b/src/tr2/game/game_flow/inventory.c index d0f9ff1dc..f4dd1ddf7 100644 --- a/src/tr2/game/game_flow/inventory.c +++ b/src/tr2/game/game_flow/inventory.c @@ -1,6 +1,6 @@ #include "game/game_flow/inventory.h" -#include "game/game_flow/common.h" +#include "decomp/savegame.h" #include "game/gun/gun.h" #include "game/inventory.h" #include "game/overlay.h" @@ -114,7 +114,7 @@ void GF_InventoryModifier_Add( void GF_InventoryModifier_Apply( const GF_LEVEL *const level, const GF_INV_TYPE type) { - START_INFO *const start = GF_GetResumeInfo(level); + START_INFO *const start = Savegame_GetCurrentInfo(level); if (!start->has_pistols && m_Add2InvItems[O_PISTOL_ITEM]) { start->has_pistols = 1; diff --git a/src/tr2/game/game_flow/sequencer.c b/src/tr2/game/game_flow/sequencer.c index 6aafe46ea..e5809a273 100644 --- a/src/tr2/game/game_flow/sequencer.c +++ b/src/tr2/game/game_flow/sequencer.c @@ -1,9 +1,11 @@ #include "game/game_flow/sequencer.h" +#include "decomp/decomp.h" #include "decomp/savegame.h" #include "game/fmv.h" #include "game/game.h" #include "game/game_flow.h" +#include "game/level.h" #include "game/music.h" #include "game/phase.h" #include "global/vars.h" @@ -16,11 +18,11 @@ GF_SEQUENCE_CONTEXT seq_ctx, void *const seq_ctx_arg) static DECLARE_EVENT_HANDLER(M_HandleExitToTitle); -static DECLARE_EVENT_HANDLER(M_HandlePicture); static DECLARE_EVENT_HANDLER(M_HandlePlayLevel); static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene); static DECLARE_EVENT_HANDLER(M_HandlePlayMusic); static DECLARE_EVENT_HANDLER(M_HandlePlayFMV); +static DECLARE_EVENT_HANDLER(M_HandlePicture); static DECLARE_EVENT_HANDLER(M_HandleLevelComplete); static DECLARE_EVENT_HANDLER(M_HandleLevelStats); static DECLARE_EVENT_HANDLER(M_HandleEnableSunset); @@ -36,24 +38,24 @@ static DECLARE_EVENT_HANDLER(M_HandleSetNumSecrets); static DECLARE_EVENT_HANDLER((*m_EventHandlers[GFS_NUMBER_OF])) = { // clang-format off - [GFS_EXIT_TO_TITLE] = M_HandleExitToTitle, - [GFS_DISPLAY_PICTURE] = M_HandlePicture, - [GFS_PLAY_LEVEL] = M_HandlePlayLevel, - [GFS_PLAY_CUTSCENE] = M_HandlePlayCutscene, - [GFS_PLAY_MUSIC] = M_HandlePlayMusic, - [GFS_PLAY_FMV] = M_HandlePlayFMV, - [GFS_LEVEL_COMPLETE] = M_HandleLevelComplete, - [GFS_LEVEL_STATS] = M_HandleLevelStats, - [GFS_TOTAL_STATS] = M_HandleTotalStats, - [GFS_ENABLE_SUNSET] = M_HandleEnableSunset, - [GFS_SET_CAMERA_ANGLE] = M_HandleSetCameraAngle, - [GFS_DISABLE_FLOOR] = M_HandleDisableFloor, - [GFS_ADD_ITEM] = M_HandleAddItem, - [GFS_ADD_SECRET_REWARD] = M_HandleAddSecretReward, - [GFS_REMOVE_WEAPONS] = M_HandleRemoveWeapons, - [GFS_REMOVE_AMMO] = M_HandleRemoveAmmo, - [GFS_SET_START_ANIM] = M_HandleSetStartAnim, - [GFS_SET_NUM_SECRETS] = M_HandleSetNumSecrets, + [GFS_EXIT_TO_TITLE] = M_HandleExitToTitle, + [GFS_PLAY_LEVEL] = M_HandlePlayLevel, + [GFS_PLAY_CUTSCENE] = M_HandlePlayCutscene, + [GFS_PLAY_MUSIC] = M_HandlePlayMusic, + [GFS_PLAY_FMV] = M_HandlePlayFMV, + [GFS_DISPLAY_PICTURE] = M_HandlePicture, + [GFS_LEVEL_COMPLETE] = M_HandleLevelComplete, + [GFS_LEVEL_STATS] = M_HandleLevelStats, + [GFS_TOTAL_STATS] = M_HandleTotalStats, + [GFS_ENABLE_SUNSET] = M_HandleEnableSunset, + [GFS_SET_CAMERA_ANGLE] = M_HandleSetCameraAngle, + [GFS_DISABLE_FLOOR] = M_HandleDisableFloor, + [GFS_ADD_ITEM] = M_HandleAddItem, + [GFS_ADD_SECRET_REWARD] = M_HandleAddSecretReward, + [GFS_REMOVE_WEAPONS] = M_HandleRemoveWeapons, + [GFS_REMOVE_AMMO] = M_HandleRemoveAmmo, + [GFS_SET_START_ANIM] = M_HandleSetStartAnim, + [GFS_SET_NUM_SECRETS] = M_HandleSetNumSecrets, // clang-format on }; @@ -62,39 +64,42 @@ static DECLARE_EVENT_HANDLER(M_HandleExitToTitle) return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } -static DECLARE_EVENT_HANDLER(M_HandlePicture) -{ - GF_COMMAND gf_cmd = { .action = GF_NOOP }; - if (seq_ctx == GFSC_SAVED) { - return gf_cmd; - } - const GF_DISPLAY_PICTURE_DATA *const data = event->data; - PHASE *const phase = Phase_Picture_Create((PHASE_PICTURE_ARGS) { - .file_name = data->path, - .display_time = data->display_time, - .fade_in_time = data->fade_in_time, - .fade_out_time = data->fade_out_time, - .display_time_includes_fades = true, - }); - gf_cmd = PhaseExecutor_Run(phase); - Phase_Picture_Destroy(phase); - return gf_cmd; -} - static DECLARE_EVENT_HANDLER(M_HandlePlayLevel) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; - if (seq_ctx != GFSC_STORY) { - if (level->type == GFL_DEMO) { - gf_cmd = GF_RunDemo(level->num); - } else if (level->type == GFL_CUTSCENE) { - gf_cmd = GF_RunCutscene(level->num); - } else { - gf_cmd = GF_RunGame(level, seq_ctx); + if (seq_ctx == GFSC_STORY) { + return gf_cmd; + } else if (level->type == GFL_DEMO) { + if (!Level_Initialise(level)) { + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } - if (gf_cmd.action == GF_LEVEL_COMPLETE) { - gf_cmd.action = GF_NOOP; + gf_cmd = GF_RunDemo(level->num); + } else if (level->type == GFL_CUTSCENE) { + if (!Level_Initialise(level)) { + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; + } + gf_cmd = GF_RunCutscene(level->num); + } else { + if (seq_ctx != GFSC_SAVED) { + if (level != nullptr) { + Savegame_ApplyLogicToCurrentInfo(level); + } + InitialiseLevelFlags(); + } + if (!Level_Initialise(level)) { + Game_SetCurrentLevel(nullptr); + GF_SetCurrentLevel(nullptr); + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; + } + if (seq_ctx == GFSC_SAVED) { + ExtractSaveGameInfo(); + } else if (level->type == GFL_NORMAL) { + GF_InventoryModifier_Apply(Game_GetCurrentLevel(), GF_INV_REGULAR); } + gf_cmd = GF_RunGame(level, seq_ctx); + } + if (gf_cmd.action == GF_LEVEL_COMPLETE) { + gf_cmd.action = GF_NOOP; } return gf_cmd; } @@ -112,12 +117,6 @@ static DECLARE_EVENT_HANDLER(M_HandlePlayCutscene) return gf_cmd; } -static DECLARE_EVENT_HANDLER(M_HandlePlayMusic) -{ - Music_Play((int32_t)(intptr_t)event->data, MPM_ALWAYS); - return (GF_COMMAND) { .action = GF_NOOP }; -} - static DECLARE_EVENT_HANDLER(M_HandlePlayFMV) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; @@ -132,29 +131,52 @@ static DECLARE_EVENT_HANDLER(M_HandlePlayFMV) return gf_cmd; } -static DECLARE_EVENT_HANDLER(M_HandleLevelComplete) +static DECLARE_EVENT_HANDLER(M_HandlePlayMusic) +{ + Music_Play((int32_t)(intptr_t)event->data, MPM_ALWAYS); + return (GF_COMMAND) { .action = GF_NOOP }; +} + +static DECLARE_EVENT_HANDLER(M_HandlePicture) { GF_COMMAND gf_cmd = { .action = GF_NOOP }; - if (seq_ctx != GFSC_NORMAL) { + if (seq_ctx == GFSC_SAVED) { return gf_cmd; } + const GF_DISPLAY_PICTURE_DATA *const data = event->data; + PHASE *const phase = Phase_Picture_Create((PHASE_PICTURE_ARGS) { + .file_name = data->path, + .display_time = data->display_time, + .fade_in_time = data->fade_in_time, + .fade_out_time = data->fade_out_time, + .display_time_includes_fades = true, + }); + gf_cmd = PhaseExecutor_Run(phase); + Phase_Picture_Destroy(phase); + return gf_cmd; +} + +static DECLARE_EVENT_HANDLER(M_HandleLevelComplete) +{ + if (seq_ctx != GFSC_NORMAL) { + return (GF_COMMAND) { .action = GF_NOOP }; + } const GF_LEVEL *const current_level = Game_GetCurrentLevel(); const GF_LEVEL *const next_level = GF_GetLevelAfter(current_level); - START_INFO *const start = GF_GetResumeInfo(current_level); + START_INFO *const start = Savegame_GetCurrentInfo(current_level); start->stats = g_SaveGame.current_stats; start->available = 0; if (next_level != nullptr) { Savegame_PersistGameToCurrentInfo(next_level); g_SaveGame.current_level = next_level->num; } - if (next_level == nullptr || gf_cmd.action != GF_NOOP) { - return gf_cmd; + if (next_level == nullptr) { + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } - gf_cmd = (GF_COMMAND) { + return (GF_COMMAND) { .action = GF_START_GAME, .param = next_level->num, }; - return gf_cmd; } static DECLARE_EVENT_HANDLER(M_HandleLevelStats) @@ -162,24 +184,25 @@ static DECLARE_EVENT_HANDLER(M_HandleLevelStats) GF_COMMAND gf_cmd = { .action = GF_NOOP }; if (seq_ctx == GFSC_NORMAL) { const GF_LEVEL *const current_level = Game_GetCurrentLevel(); - PHASE *const stats_phase = Phase_Stats_Create((PHASE_STATS_ARGS) { + PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) { .background_type = Game_IsInGym() ? BK_TRANSPARENT : BK_OBJECT, .level_num = current_level->num, .show_final_stats = false, .use_bare_style = false, }); - gf_cmd = PhaseExecutor_Run(stats_phase); - Phase_Stats_Destroy(stats_phase); + gf_cmd = PhaseExecutor_Run(phase); + Phase_Stats_Destroy(phase); } return gf_cmd; } static DECLARE_EVENT_HANDLER(M_HandleTotalStats) { - GF_COMMAND gf_cmd = { .action = GF_NOOP }; + GF_COMMAND gf_cmd = { .action = GF_EXIT_TO_TITLE }; if (seq_ctx == GFSC_NORMAL) { const GF_LEVEL *const current_level = Game_GetCurrentLevel(); - START_INFO *const start = GF_GetResumeInfo(current_level); + // TODO: move me out + START_INFO *const start = Savegame_GetCurrentInfo(current_level); start->stats = g_SaveGame.current_stats; g_SaveGame.bonus_flag = true; PHASE *const phase = Phase_Stats_Create((PHASE_STATS_ARGS) { @@ -190,8 +213,6 @@ static DECLARE_EVENT_HANDLER(M_HandleTotalStats) }); gf_cmd = PhaseExecutor_Run(phase); Phase_Stats_Destroy(phase); - } else { - gf_cmd = (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } return gf_cmd; } diff --git a/src/tr2/game/game_flow/sequencer_misc.c b/src/tr2/game/game_flow/sequencer_misc.c index 9b1149fbe..666522f81 100644 --- a/src/tr2/game/game_flow/sequencer_misc.c +++ b/src/tr2/game/game_flow/sequencer_misc.c @@ -10,12 +10,12 @@ #include -GF_COMMAND GF_TitleSequence(void) +GF_COMMAND GF_RunTitle(void) { Savegame_UnbindSlot(); GameStringTable_Apply(nullptr); const GF_LEVEL *const title_level = GF_GetTitleLevel(); - if (!Level_Initialise(title_level, GFSC_NORMAL)) { + if (!Level_Initialise(title_level)) { return (GF_COMMAND) { .action = GF_EXIT_GAME }; } return GF_ShowInventory(INV_TITLE_MODE); @@ -38,10 +38,13 @@ GF_COMMAND GF_DoLevelSequence( if (gf_cmd.action != GF_NOOP && gf_cmd.action != GF_LEVEL_COMPLETE) { return gf_cmd; } + if (g_GameFlow.is_demo_version && g_GameFlow.single_level) { + return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; + } if (Game_IsInGym()) { return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } - if (current_level->num >= level_count - 1) { + if (current_level->num + 1 >= level_count) { return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE }; } current_level++; diff --git a/src/tr2/game/lara/control.c b/src/tr2/game/lara/control.c index c78fe8ed8..03e50b4da 100644 --- a/src/tr2/game/lara/control.c +++ b/src/tr2/game/lara/control.c @@ -1,10 +1,10 @@ #include "game/lara/control.h" +#include "decomp/savegame.h" #include "decomp/skidoo.h" #include "game/camera.h" #include "game/creature.h" #include "game/game.h" -#include "game/game_flow.h" #include "game/gun/gun.h" #include "game/input.h" #include "game/inventory.h" @@ -860,7 +860,7 @@ void Lara_InitialiseInventory(const GF_LEVEL *const level) Inv_AddItem(O_COMPASS_ITEM); - START_INFO *const start = GF_GetResumeInfo(level); + START_INFO *const start = Savegame_GetCurrentInfo(level); if (start != nullptr) { if (g_GF_RemoveWeapons) { start->has_pistols = 0; @@ -969,7 +969,7 @@ void Lara_InitialiseMeshes(const GF_LEVEL *const level) Lara_SwapSingleMesh(i, O_LARA); } - const START_INFO *const start = GF_GetResumeInfo(level); + const START_INFO *const start = Savegame_GetCurrentInfo(level); if (start == nullptr) { return; } diff --git a/src/tr2/game/level.c b/src/tr2/game/level.c index 3944697d9..0542ce56a 100644 --- a/src/tr2/game/level.c +++ b/src/tr2/game/level.c @@ -14,6 +14,7 @@ #include "game/objects/setup.h" #include "game/output.h" #include "game/overlay.h" +#include "game/random.h" #include "game/render/common.h" #include "game/room.h" #include "game/shell.h" @@ -713,10 +714,14 @@ bool Level_Load(const GF_LEVEL *const level) return true; } -bool Level_Initialise( - const GF_LEVEL *const level, const GF_SEQUENCE_CONTEXT seq_ctx) +bool Level_Initialise(const GF_LEVEL *const level) { - LOG_DEBUG("num=%d type=%d seq_ctx=%d", level->num, level->type, seq_ctx); + LOG_DEBUG("num=%d type=%d", level->num, level->type); + if (level->type == GFL_DEMO) { + Random_SeedDraw(0xD371F947); + Random_SeedControl(0xD371F947); + } + if (level->type != GFL_TITLE && level->type != GFL_DEMO) { g_GymInvOpenEnabled = false; } @@ -741,21 +746,13 @@ bool Level_Initialise( if (g_Lara.item_num != NO_ITEM) { Lara_Initialise(level); } - if (level->type == GFL_NORMAL || level->type == GFL_DEMO - || seq_ctx == GFSC_SAVED) { - GetCarriedItems(); - } + GetCarriedItems(); Effect_InitialiseArray(); LOT_InitialiseArray(); Overlay_Reset(); g_HealthBarTimer = 100; Sound_StopAll(); - if (seq_ctx == GFSC_SAVED) { - ExtractSaveGameInfo(); - } else if (level->type == GFL_NORMAL) { - GF_InventoryModifier_Apply(Game_GetCurrentLevel(), GF_INV_REGULAR); - } if (g_Objects[O_FINAL_LEVEL_COUNTER].loaded) { InitialiseFinalLevel(); diff --git a/src/tr2/game/level.h b/src/tr2/game/level.h index 5a6825b92..e3754841c 100644 --- a/src/tr2/game/level.h +++ b/src/tr2/game/level.h @@ -4,6 +4,6 @@ #include -bool Level_Initialise(const GF_LEVEL *level, GF_SEQUENCE_CONTEXT seq_ctx); +bool Level_Initialise(const GF_LEVEL *level); bool Level_Load(const GF_LEVEL *level); void Level_Unload(void); diff --git a/src/tr2/game/savegame/common.c b/src/tr2/game/savegame/common.c index 64871b65b..13621e063 100644 --- a/src/tr2/game/savegame/common.c +++ b/src/tr2/game/savegame/common.c @@ -1,8 +1,36 @@ #include "decomp/savegame.h" +#include "game/game_flow.h" #include "global/vars.h" +#include +#include #include #include +#include + +void Savegame_Init(void) +{ + g_SaveGame.start = Memory_Alloc( + sizeof(START_INFO) + * (GF_GetLevelTable(GFLT_MAIN)->count + + GF_GetLevelTable(GFLT_DEMOS)->count)); + + const GF_LEVEL_TABLE *const level_table = GF_GetLevelTable(GFLT_DEMOS); + for (int32_t i = 0; i < level_table->count; i++) { + START_INFO *const resume_info = + Savegame_GetCurrentInfo(&level_table->levels[i]); + resume_info->available = 1; + resume_info->has_pistols = 1; + resume_info->pistol_ammo = 1000; + resume_info->gun_status = LGS_ARMLESS; + resume_info->gun_type = LGT_PISTOLS; + } +} + +void Savegame_Shutdown(void) +{ + Memory_FreePointer(&g_SaveGame.start); +} int32_t Savegame_GetSlotCount(void) { @@ -21,3 +49,18 @@ bool Savegame_Save(const int32_t slot_idx) GetSavedGamesList(&g_LoadGameRequester); return true; } + +START_INFO *Savegame_GetCurrentInfo(const GF_LEVEL *const level) +{ + ASSERT(g_SaveGame.start != nullptr); + ASSERT(level != nullptr); + if (GF_GetLevelTableType(level->type) == GFLT_MAIN) { + return &g_SaveGame.start[level->num]; + } else if (level->type == GFL_DEMO) { + return &g_SaveGame.start[GF_GetLevelTable(GFLT_MAIN)->count]; + } + LOG_WARNING( + "Warning: unable to get resume info for level %d (type=%s)", level->num, + ENUM_MAP_TO_STRING(GF_LEVEL_TYPE, level->type)); + return nullptr; +} diff --git a/src/tr2/game/shell/common.c b/src/tr2/game/shell/common.c index 14957c13b..6d753e532 100644 --- a/src/tr2/game/shell/common.c +++ b/src/tr2/game/shell/common.c @@ -353,6 +353,7 @@ void Shell_Main(void) GF_Load(m_CurrentGameFlowPath); GameStringTable_LoadFromFile(m_CurrentGameStringsPath); + Savegame_Init(); Savegame_InitCurrentInfo(); S_FrontEndCheck(); @@ -414,7 +415,7 @@ void Shell_Main(void) return; } } else { - gf_cmd = GF_TitleSequence(); + gf_cmd = GF_RunTitle(); } break; @@ -432,6 +433,8 @@ void Shell_Main(void) void Shell_Shutdown(void) { + SDL_DestroyWindow(g_SDLWindow); + GF_Shutdown(); GameString_Shutdown(); Console_Shutdown(); @@ -441,6 +444,8 @@ void Shell_Shutdown(void) GameBuf_Shutdown(); Config_Shutdown(); EnumMap_Shutdown(); + + SDL_Quit(); } const char *Shell_GetConfigPath(void) diff --git a/src/tr2/global/const.h b/src/tr2/global/const.h index 2d7dcca64..86a290006 100644 --- a/src/tr2/global/const.h +++ b/src/tr2/global/const.h @@ -41,6 +41,7 @@ #define MAX_REQUESTER_ITEMS 24 #define MAX_SAVE_SLOTS 16 #define MAX_ASSAULT_TIMES 10 +#define MAX_SG_BUFFER_SIZE 6272 #define DEATH_WAIT (5 * 2 * FRAMES_PER_SECOND) // = 300 #define DEATH_WAIT_INPUT (2 * FRAMES_PER_SECOND) // = 60 diff --git a/src/tr2/global/types_decomp.h b/src/tr2/global/types_decomp.h index 2f6fa2e87..cea075624 100644 --- a/src/tr2/global/types_decomp.h +++ b/src/tr2/global/types_decomp.h @@ -141,7 +141,7 @@ typedef struct { } START_INFO; typedef struct { - START_INFO start[24]; + START_INFO *start; LEVEL_STATS current_stats; int16_t current_level; bool bonus_flag; @@ -149,7 +149,7 @@ typedef struct { uint8_t num_puzzle[4]; uint8_t num_key[4]; uint16_t reserved; - char buffer[6272]; // MAX_SG_BUFFER_SIZE + char buffer[MAX_SG_BUFFER_SIZE]; } SAVEGAME_INFO; typedef struct { diff --git a/src/tr2/global/vars.c b/src/tr2/global/vars.c index d23ff8b7d..69113e03d 100644 --- a/src/tr2/global/vars.c +++ b/src/tr2/global/vars.c @@ -77,7 +77,7 @@ ASSAULT_STATS g_Assault; int32_t g_LevelItemCount; int32_t g_HealthBarTimer; int32_t g_LevelComplete; -SAVEGAME_INFO g_SaveGame; +SAVEGAME_INFO g_SaveGame = {}; LARA_INFO g_Lara; ITEM *g_LaraItem = nullptr; int16_t g_NextItemActive; diff --git a/src/tr2/meson.build b/src/tr2/meson.build index abe5bfd3d..7980e93f1 100644 --- a/src/tr2/meson.build +++ b/src/tr2/meson.build @@ -103,7 +103,6 @@ sources = [ 'game/fmv.c', 'game/game.c', 'game/game_string.c', - 'game/game_flow/common.c', 'game/game_flow/inventory.c', 'game/game_flow/sequencer.c', 'game/game_flow/sequencer_misc.c',