Added "firstInput" stat and repurposed the "fileCreatedAt" stat. (#6070)

- The "firstInput" stat is set on first input in game. Used for RTA
  Timing.
- The "fileCreatedAt" stat is now set when then save file is created
  (After the player is done entering the file name). Useful for seeding
  non rando randomizers like Mirrorworld, Enemy Randomizer, extraTraps,
  etc.
This commit is contained in:
Glought
2026-01-02 06:39:42 -08:00
committed by GitHub
parent f666444876
commit 31db9e4ade
7 changed files with 14 additions and 5 deletions

View File

@@ -97,6 +97,7 @@ typedef struct {
/* */ u32 entrancesDiscovered[SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT];
/* */ u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT];
/* */ bool rtaTiming;
/* */ uint64_t firstInput;
/* */ uint64_t fileCreatedAt;
} SohStats;

View File

@@ -299,6 +299,7 @@ void LoadStatsVersion1() {
SaveManager::Instance->LoadData("", gSaveContext.ship.stats.dungeonKeys[i]);
});
SaveManager::Instance->LoadData("rtaTiming", gSaveContext.ship.stats.rtaTiming);
SaveManager::Instance->LoadData("firstInput", gSaveContext.ship.stats.firstInput);
SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.ship.stats.fileCreatedAt);
SaveManager::Instance->LoadData("playTimer", gSaveContext.ship.stats.playTimer);
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.ship.stats.pauseTimer);
@@ -348,6 +349,7 @@ void SaveStats(SaveContext* saveContext, int sectionID, bool fullSave) {
SaveManager::Instance->SaveData("", saveContext->ship.stats.dungeonKeys[i]);
});
SaveManager::Instance->SaveData("rtaTiming", saveContext->ship.stats.rtaTiming);
SaveManager::Instance->SaveData("firstInput", saveContext->ship.stats.firstInput);
SaveManager::Instance->SaveData("fileCreatedAt", saveContext->ship.stats.fileCreatedAt);
SaveManager::Instance->SaveData("playTimer", saveContext->ship.stats.playTimer);
SaveManager::Instance->SaveData("pauseTimer", saveContext->ship.stats.pauseTimer);
@@ -694,7 +696,8 @@ void InitStats(bool isDebug) {
gSaveContext.ship.stats.dungeonKeys[dungeon] = isDebug ? 8 : 0;
}
gSaveContext.ship.stats.rtaTiming = CVarGetInteger(CVAR_GAMEPLAY_STATS("RTATiming"), 0);
gSaveContext.ship.stats.fileCreatedAt = 0;
gSaveContext.ship.stats.fileCreatedAt = GetUnixTimestamp();
gSaveContext.ship.stats.firstInput = 0;
gSaveContext.ship.stats.playTimer = 0;
gSaveContext.ship.stats.pauseTimer = 0;
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.ship.stats.itemTimestamp); timestamp++) {

View File

@@ -22,9 +22,9 @@ char* GameplayStats_GetCurrentTime();
#define GAMEPLAYSTAT_TOTAL_TIME \
(gSaveContext.ship.stats.rtaTiming \
? (!gSaveContext.ship.stats.gameComplete \
? (!gSaveContext.ship.stats.fileCreatedAt \
? (!gSaveContext.ship.stats.firstInput \
? 0 \
: ((GetUnixTimestamp() - gSaveContext.ship.stats.fileCreatedAt) / 100)) \
: ((GetUnixTimestamp() - gSaveContext.ship.stats.firstInput) / 100)) \
: (gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] \
? gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] \
: gSaveContext.ship.stats.itemTimestamp[TIMESTAMP_TRIFORCE_COMPLETED])) \

View File

@@ -93,12 +93,14 @@ inline void from_json(const json& j, Inventory& inventory) {
inline void to_json(json& j, const SohStats& sohStats) {
j = json{
{ "entrancesDiscovered", sohStats.entrancesDiscovered },
{ "firstInput", sohStats.firstInput },
{ "fileCreatedAt", sohStats.fileCreatedAt },
};
}
inline void from_json(const json& j, SohStats& sohStats) {
j.at("entrancesDiscovered").get_to(sohStats.entrancesDiscovered);
j.at("firstInput").get_to(sohStats.firstInput);
j.at("fileCreatedAt").get_to(sohStats.fileCreatedAt);
}

View File

@@ -189,6 +189,7 @@ void Anchor::HandlePacket_UpdateTeamState(nlohmann::json payload) {
gSaveContext.gsFlags[i] = loadedData.gsFlags[i];
}
gSaveContext.ship.stats.firstInput = loadedData.ship.stats.firstInput;
gSaveContext.ship.stats.fileCreatedAt = loadedData.ship.stats.fileCreatedAt;
// Restore master sword state

View File

@@ -1631,6 +1631,7 @@ void SaveManager::LoadBaseVersion2() {
SaveManager::Instance->LoadData("", gSaveContext.ship.stats.dungeonKeys[i]);
});
SaveManager::Instance->LoadData("rtaTiming", gSaveContext.ship.stats.rtaTiming);
SaveManager::Instance->LoadData("firstInput", gSaveContext.ship.stats.firstInput);
SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.ship.stats.fileCreatedAt);
SaveManager::Instance->LoadData("playTimer", gSaveContext.ship.stats.playTimer);
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.ship.stats.pauseTimer);
@@ -1848,6 +1849,7 @@ void SaveManager::LoadBaseVersion3() {
SaveManager::Instance->LoadData("", gSaveContext.ship.stats.dungeonKeys[i]);
});
SaveManager::Instance->LoadData("rtaTiming", gSaveContext.ship.stats.rtaTiming);
SaveManager::Instance->LoadData("firstInput", gSaveContext.ship.stats.firstInput);
SaveManager::Instance->LoadData("fileCreatedAt", gSaveContext.ship.stats.fileCreatedAt);
SaveManager::Instance->LoadData("playTimer", gSaveContext.ship.stats.playTimer);
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.ship.stats.pauseTimer);

View File

@@ -781,10 +781,10 @@ void Play_Update(PlayState* play) {
}
// Start RTA timing on first non-c-up input after intro cutscene
if (!gSaveContext.ship.stats.fileCreatedAt && !Player_InCsMode(play) &&
if (!gSaveContext.ship.stats.firstInput && !Player_InCsMode(play) &&
((input[0].press.button && input[0].press.button != 0x8) || input[0].rel.stick_x != 0 ||
input[0].rel.stick_y != 0)) {
gSaveContext.ship.stats.fileCreatedAt = GetUnixTimestamp();
gSaveContext.ship.stats.firstInput = GetUnixTimestamp();
}
}
// #endregion