mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
tr2/game_flow: implement bonus level support
This implements bonus level handling the game flow for TR2, similar to TR1. Resolves #2668.
This commit is contained in:
parent
9191c91407
commit
3d47493af6
11 changed files with 45 additions and 8 deletions
|
@ -622,6 +622,7 @@
|
|||
"STATS_ASSAULT_NO_TIMES_SET": "No Times Set",
|
||||
"STATS_ASSAULT_TITLE": "BEST TIMES",
|
||||
"STATS_BASIC_FMT": "%d",
|
||||
"STATS_BONUS_STATISTICS": "Bonus Statistics",
|
||||
"STATS_DETAIL_FMT": "%d of %d",
|
||||
"STATS_DISTANCE_TRAVELLED": "Distance Travelled",
|
||||
"STATS_FINAL_STATISTICS": "Final Statistics",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-0.10...develop) - ××××-××-××
|
||||
- added the bonus level game flow type, which allows for levels to be unlocked if all main game secrets are found (#2668)
|
||||
- fixed the final two levels not allowing for secrets to be counted in the statistics (#1582)
|
||||
- removed the need to specify in the game flow levels that have no secrets (secrets will be automatically counted) (#1582)
|
||||
|
||||
|
|
|
@ -182,6 +182,7 @@ game with new enhancements and features.
|
|||
- added ability to skip FMVs with both the Action key
|
||||
- added ability to skip end credits with the Action and Escape keys
|
||||
- added the ability to specify per-level SFX files rather than enforcing the default (main.sfx) on all levels
|
||||
- added the ability to define bonus levels in the game flow, which unlock when all main game secrets are found
|
||||
- added -l/--level and -s/--save command line arguments
|
||||
- expanded internal game memory limit from 7.5 MB to unlimited (within system memory cap)
|
||||
- expanded maximum object textures from 2048 to unlimited (within game's overall memory cap)
|
||||
|
|
|
@ -52,3 +52,4 @@ CFG_INT32(g_Config, audio.sound_volume, 10)
|
|||
CFG_INT32(g_Config, audio.music_volume, 10)
|
||||
CFG_BOOL(g_Config, audio.enable_lara_mic, false)
|
||||
CFG_ENUM(g_Config, audio.underwater_music_mode, UMM_FULL, UNDERWATER_MUSIC_MODE)
|
||||
CFG_BOOL(g_Config, profile.bonus_level_unlock, false)
|
||||
|
|
|
@ -109,4 +109,8 @@ typedef struct {
|
|||
int32_t scaler;
|
||||
float sizer;
|
||||
} rendering;
|
||||
|
||||
struct {
|
||||
bool bonus_level_unlock;
|
||||
} profile;
|
||||
} CONFIG;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "game/stats.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/config.h>
|
||||
#include <libtrx/debug.h>
|
||||
|
||||
static DECLARE_GF_EVENT_HANDLER(M_HandlePlayLevel);
|
||||
|
@ -80,7 +81,7 @@ static DECLARE_GF_EVENT_HANDLER(M_HandlePlayLevel)
|
|||
|
||||
default:
|
||||
Savegame_ApplyLogicToCurrentInfo(level);
|
||||
if (level->type == GFL_NORMAL) {
|
||||
if (level->type == GFL_NORMAL || level->type == GFL_BONUS) {
|
||||
GF_InventoryModifier_Scan(level);
|
||||
GF_InventoryModifier_Apply(level, GF_INV_REGULAR);
|
||||
Stats_Reset();
|
||||
|
@ -117,7 +118,7 @@ static DECLARE_GF_EVENT_HANDLER(M_HandlePlayLevel)
|
|||
break;
|
||||
|
||||
default:
|
||||
if (level->type == GFL_NORMAL) {
|
||||
if (level->type == GFL_NORMAL || level->type == GFL_BONUS) {
|
||||
GF_InventoryModifier_Scan(Game_GetCurrentLevel());
|
||||
GF_InventoryModifier_Apply(Game_GetCurrentLevel(), GF_INV_REGULAR);
|
||||
}
|
||||
|
@ -141,6 +142,12 @@ static DECLARE_GF_EVENT_HANDLER(M_HandlePlayLevel)
|
|||
gf_cmd = GF_RunGame(level, seq_ctx);
|
||||
}
|
||||
if (gf_cmd.action == GF_LEVEL_COMPLETE) {
|
||||
// TODO: refactor, currently required to guarantee final statistics are
|
||||
// accurate prior to jumping to a bonus level.
|
||||
if (level->type == GFL_NORMAL || level->type == GFL_BONUS) {
|
||||
START_INFO *const start = Savegame_GetCurrentInfo(level);
|
||||
start->stats = g_SaveGame.current_stats;
|
||||
}
|
||||
gf_cmd.action = GF_NOOP;
|
||||
}
|
||||
return gf_cmd;
|
||||
|
@ -170,6 +177,9 @@ static DECLARE_GF_EVENT_HANDLER(M_HandleLevelComplete)
|
|||
START_INFO *const start = Savegame_GetCurrentInfo(current_level);
|
||||
start->stats = g_SaveGame.current_stats;
|
||||
start->available = 0;
|
||||
g_Config.profile.bonus_level_unlock =
|
||||
Stats_CheckAllSecretsCollected(GFL_NORMAL);
|
||||
|
||||
if (next_level != nullptr) {
|
||||
Savegame_PersistGameToCurrentInfo(next_level);
|
||||
g_SaveGame.current_level = next_level->num;
|
||||
|
@ -177,6 +187,9 @@ static DECLARE_GF_EVENT_HANDLER(M_HandleLevelComplete)
|
|||
if (next_level == nullptr) {
|
||||
return (GF_COMMAND) { .action = GF_NOOP };
|
||||
}
|
||||
if (next_level->type == GFL_BONUS && !g_Config.profile.bonus_level_unlock) {
|
||||
return (GF_COMMAND) { .action = GF_EXIT_TO_TITLE };
|
||||
}
|
||||
return (GF_COMMAND) {
|
||||
.action = GF_START_GAME,
|
||||
.param = next_level->num,
|
||||
|
|
|
@ -17,6 +17,7 @@ GS_DEFINE(OSD_SAVE_GAME, "Saved game to save slot %d")
|
|||
GS_DEFINE(KEYMAP_USE_FLARE, "Flare")
|
||||
GS_DEFINE(KEYMAP_CAMERA_RESET, "Camera Reset")
|
||||
GS_DEFINE(STATS_TIME_TAKEN, "Time Taken")
|
||||
GS_DEFINE(STATS_BONUS_STATISTICS, "Bonus Statistics")
|
||||
GS_DEFINE(STATS_SECRETS, "Secrets Found")
|
||||
GS_DEFINE(STATS_AMMO_USED, "Ammo Used")
|
||||
GS_DEFINE(STATS_AMMO_HITS, "Hits")
|
||||
|
|
|
@ -817,7 +817,8 @@ void Lara_Initialise(const GF_LEVEL *const level)
|
|||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.creature = nullptr;
|
||||
|
||||
if (level->type == GFL_NORMAL && g_GF_LaraStartAnim) {
|
||||
if ((level->type == GFL_NORMAL || level->type == GFL_BONUS)
|
||||
&& g_GF_LaraStartAnim) {
|
||||
g_Lara.water_status = LWS_ABOVE_WATER;
|
||||
g_Lara.gun_status = LGS_HANDS_BUSY;
|
||||
Item_SwitchToObjAnim(item, LA_EXTRA_BREATH, 0, O_LARA_EXTRA);
|
||||
|
|
|
@ -38,14 +38,14 @@ void Stats_UpdateTimer(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
FINAL_STATS Stats_ComputeFinalStats(void)
|
||||
FINAL_STATS Stats_ComputeFinalStats(GF_LEVEL_TYPE level_type)
|
||||
{
|
||||
FINAL_STATS result = {};
|
||||
|
||||
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];
|
||||
if (level->type == GFL_GYM) {
|
||||
if (level->type != level_type) {
|
||||
continue;
|
||||
}
|
||||
result.timer += g_SaveGame.start[i].stats.timer;
|
||||
|
@ -109,3 +109,9 @@ int32_t Stats_GetSecrets(void)
|
|||
{
|
||||
return m_LevelSecrets;
|
||||
}
|
||||
|
||||
bool Stats_CheckAllSecretsCollected(GF_LEVEL_TYPE level_type)
|
||||
{
|
||||
const FINAL_STATS stats = Stats_ComputeFinalStats(level_type);
|
||||
return stats.found_secrets >= stats.total_secrets;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <libtrx/game/game_flow.h>
|
||||
|
||||
void Stats_StartTimer(void);
|
||||
void Stats_UpdateTimer(void);
|
||||
void Stats_Reset(void);
|
||||
void Stats_CalculateStats(void);
|
||||
int32_t Stats_GetSecrets(void);
|
||||
FINAL_STATS Stats_ComputeFinalStats(void);
|
||||
FINAL_STATS Stats_ComputeFinalStats(GF_LEVEL_TYPE level_type);
|
||||
bool Stats_CheckAllSecretsCollected(GF_LEVEL_TYPE level_type);
|
||||
|
|
|
@ -38,6 +38,7 @@ typedef struct {
|
|||
UI_STATS_DIALOG_ARGS args;
|
||||
UI_WIDGET *requester;
|
||||
int32_t listener;
|
||||
GF_LEVEL_TYPE level_type;
|
||||
} UI_STATS_DIALOG;
|
||||
|
||||
static void M_AddRow(
|
||||
|
@ -168,7 +169,7 @@ static void M_AddLevelStatsRows(UI_STATS_DIALOG *const self)
|
|||
|
||||
static void M_AddFinalStatsRows(UI_STATS_DIALOG *const self)
|
||||
{
|
||||
const FINAL_STATS final_stats = Stats_ComputeFinalStats();
|
||||
const FINAL_STATS final_stats = Stats_ComputeFinalStats(self->level_type);
|
||||
const STATS_COMMON *stats = (STATS_COMMON *)&final_stats;
|
||||
M_AddRowFromRole(self, M_ROW_TIMER, stats);
|
||||
M_AddRowFromRole(self, M_ROW_ALL_SECRETS, stats);
|
||||
|
@ -296,6 +297,7 @@ UI_WIDGET *UI_StatsDialog_Create(UI_STATS_DIALOG_ARGS args)
|
|||
ASSERT(args.style == UI_STATS_DIALOG_STYLE_BORDERED);
|
||||
|
||||
self->args = args;
|
||||
self->level_type = GF_GetLevel(GFLT_MAIN, self->args.level_num)->type;
|
||||
self->requester = UI_Requester_Create((UI_REQUESTER_SETTINGS) {
|
||||
.is_selectable = false,
|
||||
.visible_rows = VISIBLE_ROWS,
|
||||
|
@ -314,7 +316,10 @@ UI_WIDGET *UI_StatsDialog_Create(UI_STATS_DIALOG_ARGS args)
|
|||
break;
|
||||
|
||||
case UI_STATS_DIALOG_MODE_FINAL:
|
||||
UI_Requester_SetTitle(self->requester, GS(STATS_FINAL_STATISTICS));
|
||||
const char *title = self->level_type == GFL_BONUS
|
||||
? GS(STATS_BONUS_STATISTICS)
|
||||
: GS(STATS_FINAL_STATISTICS);
|
||||
UI_Requester_SetTitle(self->requester, title);
|
||||
M_AddFinalStatsRows(self);
|
||||
break;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue