tr2/stats: allow two of each secret type
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / Build TR1 (push) Has been skipped
Publish a pre-release / Build TR2 (push) Has been skipped
Publish a pre-release / Create a prerelease (push) Has been skipped

This allows custom levels to define up to two of each secret type per
level, and continue to have statistics accurately captured.

Resolves #2674.
This commit is contained in:
lahm86 2025-03-22 14:09:39 +00:00
parent 46892814fb
commit 5727910c6e
7 changed files with 57 additions and 26 deletions

View file

@ -1,5 +1,6 @@
## [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)
- added the ability for custom levels to have up to two of each secret type per level (#2674)
- 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)

View file

@ -141,6 +141,7 @@ game with new enhancements and features.
- added optional fade effects to the hardware renderer
- added text information when changing rendering options at runtime
- added support for animated sprites
- added the ability for custom levels to have up to two of each secret type per level
- changed the hardware renderer to always use 16-bit textures
- changed the software renderer to use the picture's palette for the background pictures
- changed fullscreen behavior to use windowed desktop mode

View file

@ -3,6 +3,7 @@
#include "game/inventory_ring.h"
#include "game/items.h"
#include "game/objects/vars.h"
#include "game/stats.h"
#include "global/vars.h"
#include <libtrx/utils.h>
@ -212,15 +213,9 @@ bool Inv_AddItem(const GAME_OBJECT_ID obj_id)
return true;
case O_SECRET_1:
g_SaveGame.current_stats.secret_flags |= 1;
return true;
case O_SECRET_2:
g_SaveGame.current_stats.secret_flags |= 2;
return true;
case O_SECRET_3:
g_SaveGame.current_stats.secret_flags |= 4;
Stats_MarkSecretCollected(obj_id);
return true;
case O_KEY_ITEM_1:

View file

@ -15,6 +15,7 @@
#include "game/output.h"
#include "game/overlay.h"
#include "game/room.h"
#include "game/stats.h"
#include "global/vars.h"
#include <libtrx/config.h>
@ -82,10 +83,7 @@ static void M_DoPickup(const int16_t item_num)
Inv_AddItem(item->object_id);
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)
>= 3) {
&& Stats_CheckAllLevelSecretsCollected()) {
GF_InventoryModifier_Apply(Game_GetCurrentLevel(), GF_INV_SECRET);
}

View file

@ -12,6 +12,8 @@
static int32_t m_CachedItemCount = 0;
static int32_t m_LevelSecrets = 0;
static bool M_SetSecretFlag(uint8_t *flags, GAME_OBJECT_ID obj_id);
#if USE_REAL_CLOCK
static CLOCK_TIMER m_StartCounter = { .type = CLOCK_TYPE_REAL };
static int32_t m_StartTimer = 0;
@ -38,6 +40,19 @@ void Stats_UpdateTimer(void)
}
#endif
static bool M_SetSecretFlag(uint8_t *const flags, const GAME_OBJECT_ID obj_id)
{
for (int32_t i = 0; i < 2; i++) {
const int32_t flag = 1 << ((obj_id - O_SECRET_1) + i * 3);
if ((*flags & flag) == 0) {
*flags |= flag;
return true;
}
}
return false;
}
FINAL_STATS Stats_ComputeFinalStats(GF_LEVEL_TYPE level_type)
{
FINAL_STATS result = {};
@ -86,7 +101,7 @@ void Stats_ObserveItemsLoad(void)
void Stats_CalculateStats(void)
{
m_LevelSecrets = 0;
int32_t secret_flags = 0;
uint8_t secret_flags = 0;
for (int32_t i = 0; i < m_CachedItemCount; i++) {
const ITEM *const item = Item_Get(i);
@ -95,12 +110,9 @@ void Stats_CalculateStats(void)
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;
}
if (Object_IsType(item->object_id, g_SecretObjects)
&& M_SetSecretFlag(&secret_flags, item->object_id)) {
m_LevelSecrets++;
}
}
}
@ -110,6 +122,23 @@ int32_t Stats_GetSecrets(void)
return m_LevelSecrets;
}
void Stats_MarkSecretCollected(const GAME_OBJECT_ID obj_id)
{
M_SetSecretFlag(&g_SaveGame.current_stats.secret_flags, obj_id);
}
bool Stats_CheckAllLevelSecretsCollected(void)
{
int32_t flags = g_SaveGame.current_stats.secret_flags;
int32_t count = 0;
while (flags != 0) {
count += flags & 1;
flags >>= 1;
}
return count >= g_SaveGame.current_stats.max_secret_count;
}
bool Stats_CheckAllSecretsCollected(GF_LEVEL_TYPE level_type)
{
const FINAL_STATS stats = Stats_ComputeFinalStats(level_type);

View file

@ -9,5 +9,7 @@ void Stats_UpdateTimer(void);
void Stats_Reset(void);
void Stats_CalculateStats(void);
int32_t Stats_GetSecrets(void);
void Stats_MarkSecretCollected(GAME_OBJECT_ID obj_id);
bool Stats_CheckAllLevelSecretsCollected(void);
FINAL_STATS Stats_ComputeFinalStats(GF_LEVEL_TYPE level_type);
bool Stats_CheckAllSecretsCollected(GF_LEVEL_TYPE level_type);

View file

@ -72,7 +72,7 @@ static void M_AddRowFromRole(
UI_STATS_DIALOG *const self, const M_ROW_ROLE role,
const STATS_COMMON *const stats)
{
char buf[32];
char buf[64];
switch (role) {
case M_ROW_TIMER: {
@ -86,15 +86,20 @@ static void M_AddRowFromRole(
case M_ROW_LEVEL_SECRETS: {
char *ptr = buf;
int32_t num_secrets = 0;
for (int32_t i = 0; i < 3; i++) {
if (((LEVEL_STATS *)stats)->secret_flags & (1 << i)) {
sprintf(ptr, "\\{secret %d}", i + 1);
num_secrets++;
} else {
strcpy(ptr, " ");
const LEVEL_STATS *const level_stats = (LEVEL_STATS *)stats;
for (int32_t i = 1; i >= 0; i--) {
for (int32_t j = 0; j < 3; j++) {
const int32_t flag = 1 << (j + i * 3);
if ((level_stats->secret_flags & flag) != 0) {
sprintf(ptr, "\\{secret %d}", j + 1);
num_secrets++;
} else {
strcpy(ptr, " ");
}
ptr += strlen(ptr);
}
ptr += strlen(ptr);
}
*ptr++ = '\0';
if (num_secrets == 0) {
strcpy(buf, GS(MISC_NONE));