tr2/stats: determine secret count automatically

This fixes the hardcoded check to skip secret counts in statistics for
the final two levels and allows to do away with the game flow's secret
count mechanic, by counting the number of secret pickups in the level
and storing in the stats.

Resolves #1582.
This commit is contained in:
lahm86 2025-03-20 14:59:26 +00:00
parent e7809774f4
commit fc4064582d
12 changed files with 73 additions and 11 deletions

View file

@ -1,4 +1,5 @@
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-0.10...develop) - ××××-××-××
- fixed the final two levels not allowing for secrets to be counted in the statistics (#1582)
## [0.10](https://github.com/LostArtefacts/TRX/compare/tr2-0.9.2...tr2-0.10) - 2025-03-18
- added support for 60 FPS rendering

View file

@ -128,6 +128,7 @@ game with new enhancements and features.
#### Statistics
- fixed the dragon counting as more than one kill if allowed to revive
- fixed enemies that are run over by the skidoo not being counted in the statistics
- fixed the final two levels not allowing for secrets to be counted in the statistics
#### Visuals
- added quadrilateral texture correction

View file

@ -126,6 +126,14 @@ static DECLARE_GF_EVENT_HANDLER(M_HandlePlayLevel)
break;
}
Stats_CalculateStats();
START_INFO *const resume = Savegame_GetCurrentInfo(level);
if (resume != nullptr) {
const int32_t secret_count = Stats_GetSecrets();
resume->stats.max_secret_count = secret_count;
g_SaveGame.current_stats.max_secret_count = secret_count;
}
ASSERT(GF_GetCurrentLevel() == level);
if (level->type == GFL_DEMO) {
gf_cmd = GF_RunDemo(level->num);

View file

@ -18,6 +18,7 @@
#include "game/room.h"
#include "game/shell.h"
#include "game/sound.h"
#include "game/stats.h"
#include "global/const.h"
#include "global/vars.h"
@ -168,6 +169,8 @@ static void M_LoadFromFile(const GF_LEVEL *const level)
Level_ReadPathingData(file);
Level_ReadAnimatedTextureRanges(file);
Level_ReadItems(file);
// TODO: perhaps call from Level_LoadObjectsAndItems
Stats_ObserveItemsLoad();
Level_ReadLightMap(file);
Level_ReadCinematicFrames(file);

View file

@ -11,6 +11,7 @@
#include "game/lara/control.h"
#include "game/lara/misc.h"
#include "game/objects/common.h"
#include "game/objects/vars.h"
#include "game/output.h"
#include "game/overlay.h"
#include "game/room.h"
@ -80,8 +81,7 @@ static void M_DoPickup(const int16_t item_num)
Overlay_AddDisplayPickup(item->object_id);
Inv_AddItem(item->object_id);
if ((item->object_id == O_SECRET_1 || item->object_id == O_SECRET_2
|| item->object_id == O_SECRET_3)
if (Object_IsType(item->object_id, g_SecretObjects)
&& (g_SaveGame.current_stats.secret_flags & 1)
+ ((g_SaveGame.current_stats.secret_flags >> 1) & 1)
+ ((g_SaveGame.current_stats.secret_flags >> 2) & 1)

View file

@ -107,6 +107,15 @@ const GAME_OBJECT_ID g_PickupObjects[] = {
// clang-format on
};
const GAME_OBJECT_ID g_SecretObjects[] = {
// clang-format off
O_SECRET_1,
O_SECRET_2,
O_SECRET_3,
NO_OBJECT,
// clang-format on
};
const GAME_OBJECT_ID g_DoorObjects[] = {
// clang-format off
O_DOOR_TYPE_1,

View file

@ -4,3 +4,4 @@
extern const GAME_OBJECT_ID g_DoorObjects[];
extern const GAME_OBJECT_ID g_TrapdoorObjects[];
extern const GAME_OBJECT_ID g_SecretObjects[];

View file

@ -8,6 +8,7 @@
#include "game/inventory.h"
#include "game/music.h"
#include "game/objects/common.h"
#include "game/objects/vars.h"
#include "game/output.h"
#include "game/scaler.h"
#include "game/text.h"
@ -523,7 +524,7 @@ static void M_DrawPickups(void)
void Overlay_AddDisplayPickup(const GAME_OBJECT_ID obj_id)
{
if (obj_id == O_SECRET_1 || obj_id == O_SECRET_2 || obj_id == O_SECRET_3) {
if (Object_IsType(obj_id, g_SecretObjects)) {
Music_Play(g_GameFlow.secret_track, MPM_ALWAYS);
}

View file

@ -2,12 +2,16 @@
#include "game/clock.h"
#include "game/game_flow.h"
#include "game/objects/vars.h"
#include "global/vars.h"
#include <libtrx/log.h>
#define USE_REAL_CLOCK 0
static int32_t m_CachedItemCount = 0;
static int32_t m_LevelSecrets = 0;
#if USE_REAL_CLOCK
static CLOCK_TIMER m_StartCounter = { .type = CLOCK_TYPE_REAL };
static int32_t m_StartTimer = 0;
@ -51,14 +55,12 @@ FINAL_STATS Stats_ComputeFinalStats(void)
result.distance += g_SaveGame.start[i].stats.distance;
result.medipacks += g_SaveGame.start[i].stats.medipacks;
// TODO: #170, consult GFE_NUM_SECRETS rather than hardcoding this
if (i < level_table->count - 2) {
for (int32_t j = 0; j < 3; j++) {
if (g_SaveGame.start[i].stats.secret_flags & (1 << j)) {
result.found_secrets++;
}
result.total_secrets++;
for (int32_t j = 0; j < g_SaveGame.start[i].stats.max_secret_count;
j++) {
if (g_SaveGame.start[i].stats.secret_flags & (1 << j)) {
result.found_secrets++;
}
result.total_secrets++;
}
}
@ -75,3 +77,35 @@ void Stats_Reset(void)
g_SaveGame.current_stats.medipacks = 0;
g_SaveGame.current_stats.secret_flags = 0;
}
void Stats_ObserveItemsLoad(void)
{
m_CachedItemCount = Item_GetLevelCount();
}
void Stats_CalculateStats(void)
{
m_LevelSecrets = 0;
int32_t secret_flags = 0;
for (int32_t i = 0; i < m_CachedItemCount; i++) {
const ITEM *const item = Item_Get(i);
if (item->object_id < 0 || item->object_id >= O_NUMBER_OF) {
LOG_ERROR("Bad Object number (%d) on Item %d", item->object_id, i);
continue;
}
if (Object_IsType(item->object_id, g_SecretObjects)) {
const int32_t flag = 1 << (item->object_id - O_SECRET_1);
if ((secret_flags & flag) == 0) {
m_LevelSecrets++;
secret_flags |= flag;
}
}
}
}
int32_t Stats_GetSecrets(void)
{
return m_LevelSecrets;
}

View file

@ -5,4 +5,7 @@
void Stats_StartTimer(void);
void Stats_UpdateTimer(void);
void Stats_Reset(void);
void Stats_ObserveItemsLoad(void);
void Stats_CalculateStats(void);
int32_t Stats_GetSecrets(void);
FINAL_STATS Stats_ComputeFinalStats(void);

View file

@ -156,7 +156,7 @@ static void M_AddLevelStatsRows(UI_STATS_DIALOG *const self)
? (STATS_COMMON *)&g_SaveGame.current_stats
: (STATS_COMMON *)&g_SaveGame.start[self->args.level_num].stats;
M_AddRowFromRole(self, M_ROW_TIMER, stats);
if (g_GF_NumSecrets != 0) {
if (stats->max_secret_count != 0) {
M_AddRowFromRole(self, M_ROW_LEVEL_SECRETS, stats);
}
M_AddRowFromRole(self, M_ROW_KILLS, stats);

View file

@ -87,6 +87,7 @@ typedef struct STATS_COMMON {
uint32_t distance;
uint16_t kills;
uint8_t medipacks;
uint16_t max_secret_count;
} STATS_COMMON;
typedef struct {