mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-29 12:52:18 -05:00
Enhancement: Room/Scene Timers (#2478)
* Groundwork on scene/room timers; naming changes * added to save manager; reworked storing timestamps * actually saved stuff to savemanager; accounted for null playstate * finally fixed the fucking timers * Added scene mapping * Added CVar for room/scene level; fixed some displays * reworked logic * increase name spec for scene timestamps * Actually save item timestamps when loading v3 save * Cleanup * fix merge artifact * apply suggestions
This commit is contained in:
parent
0f40472c1a
commit
ff1d8a9e9d
@ -53,6 +53,14 @@ typedef struct {
|
|||||||
/* 0x5C */ s16 gsTokens;
|
/* 0x5C */ s16 gsTokens;
|
||||||
} Inventory; // size = 0x5E
|
} Inventory; // size = 0x5E
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u16 scene;
|
||||||
|
u8 room;
|
||||||
|
u32 sceneTime;
|
||||||
|
u32 roomTime;
|
||||||
|
u8 isRoom;
|
||||||
|
} SceneTimestamp;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* */ char buildVersion[50];
|
/* */ char buildVersion[50];
|
||||||
/* */ s16 buildVersionMajor;
|
/* */ s16 buildVersionMajor;
|
||||||
@ -63,8 +71,14 @@ typedef struct {
|
|||||||
/* */ u8 dungeonKeys[19];
|
/* */ u8 dungeonKeys[19];
|
||||||
/* */ u32 playTimer;
|
/* */ u32 playTimer;
|
||||||
/* */ u32 pauseTimer;
|
/* */ u32 pauseTimer;
|
||||||
|
/* */ u32 sceneTimer;
|
||||||
|
/* */ u32 roomTimer;
|
||||||
|
/* */ s16 sceneNum;
|
||||||
|
/* */ s8 roomNum;
|
||||||
/* */ bool gameComplete;
|
/* */ bool gameComplete;
|
||||||
/* */ u32 timestamp[TIMESTAMP_MAX];
|
/* */ u32 itemTimestamp[TIMESTAMP_MAX];
|
||||||
|
/* */ SceneTimestamp sceneTimestamps[8191];
|
||||||
|
/* */ u32 tsIdx;
|
||||||
/* */ u32 count[COUNT_MAX];
|
/* */ u32 count[COUNT_MAX];
|
||||||
/* */ u32 entrancesDiscovered[SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT];
|
/* */ u32 entrancesDiscovered[SAVEFILE_ENTRANCES_DISCOVERED_IDX_COUNT];
|
||||||
/* */ u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT];
|
/* */ u32 scenesDiscovered[SAVEFILE_SCENES_DISCOVERED_IDX_COUNT];
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include "ImGuiImpl.h"
|
#include "ImGuiImpl.h"
|
||||||
#include "../UIWidgets.hpp"
|
#include "../UIWidgets.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <libultraship/bridge.h>
|
#include <libultraship/bridge.h>
|
||||||
#include <Hooks.h>
|
#include <Hooks.h>
|
||||||
@ -11,8 +11,123 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include <z64.h>
|
#include <z64.h>
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
|
extern PlayState* gPlayState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> sceneMappings = {
|
||||||
|
{"Inside the Deku Tree"},
|
||||||
|
{"Dodongo's Cavern"},
|
||||||
|
{"Inside Jabu-Jabu's Belly"},
|
||||||
|
{"Forest Temple"},
|
||||||
|
{"Fire Temple"},
|
||||||
|
{"Water Temple"},
|
||||||
|
{"Spirit Temple"},
|
||||||
|
{"Shadow Temple"},
|
||||||
|
{"Bottom of the Well"},
|
||||||
|
{"Ice Cavern"},
|
||||||
|
{"Ganon's Tower"},
|
||||||
|
{"Gerudo Training Ground"},
|
||||||
|
{"Theives' Hideout"},
|
||||||
|
{"Inside Ganon's Castle"},
|
||||||
|
{"Tower Collapse"},
|
||||||
|
{"Castle Collapse"},
|
||||||
|
{"Treasure Box Shop"},
|
||||||
|
{"Gohma's Lair"},
|
||||||
|
{"King Dodongo's Lair"},
|
||||||
|
{"Barinade's Lair"},
|
||||||
|
{"Phantom Ganon's Lair"},
|
||||||
|
{"Volvagia's Lair"},
|
||||||
|
{"Morpha's Lair"},
|
||||||
|
{"Twinrova's Lair"},
|
||||||
|
{"Bongo Bongo's Lair"},
|
||||||
|
{"Ganondorf's Lair"},
|
||||||
|
{"Ganon's Lair"},
|
||||||
|
{"Market Entrance (Day)"},
|
||||||
|
{"Market Entrance (Night)"},
|
||||||
|
{"Market Entrance (Adult)"},
|
||||||
|
{"Back Alley (Day)"},
|
||||||
|
{"Back Alley (Night)"},
|
||||||
|
{"Market (Day)"},
|
||||||
|
{"Market (Night)"},
|
||||||
|
{"Market (Adult)"},
|
||||||
|
{"Outside ToT (Day)"},
|
||||||
|
{"Outside ToT (Night)"},
|
||||||
|
{"Outside ToT (Adult)"},
|
||||||
|
{"Know-It-All Bros' House"},
|
||||||
|
{"Twins' House"},
|
||||||
|
{"Mido's House"},
|
||||||
|
{"Saria's House"},
|
||||||
|
{"Carpenter Boss's House"},
|
||||||
|
{"Man in Green's House"},
|
||||||
|
{"Bazaar"},
|
||||||
|
{"Kokiri Shop"},
|
||||||
|
{"Goron Shop"},
|
||||||
|
{"Zora Shop"},
|
||||||
|
{"Kakariko Potion Shop"},
|
||||||
|
{"Market Potion Shop"},
|
||||||
|
{"Bombchu Shop"},
|
||||||
|
{"Happy Mask Shop"},
|
||||||
|
{"Link's House"},
|
||||||
|
{"Richard's House"},
|
||||||
|
{"Stable"},
|
||||||
|
{"Impa's House"},
|
||||||
|
{"Lakeside Lab"},
|
||||||
|
{"Carpenters' Tent"},
|
||||||
|
{"Gravekeeper's Hut"},
|
||||||
|
{"Great Fairy"},
|
||||||
|
{"Fairy Fountain"},
|
||||||
|
{"Great Fairy"},
|
||||||
|
{"Grotto"},
|
||||||
|
{"Redead Grave"},
|
||||||
|
{"Fairy Fountain Grave"},
|
||||||
|
{"Royal Family's Tomb"},
|
||||||
|
{"Shooting Gallery"},
|
||||||
|
{"Temple of Time"},
|
||||||
|
{"Chamber of Sages"},
|
||||||
|
{"Castle Maze (Day)"},
|
||||||
|
{"Castle Maze (Night)"},
|
||||||
|
{"Cutscene Map"},
|
||||||
|
{"Dampe's Grave"},
|
||||||
|
{"Fishing Pond"},
|
||||||
|
{"Castle Courtyard"},
|
||||||
|
{"Bombchu Bowling Alley"},
|
||||||
|
{"Ranch House"},
|
||||||
|
{"Guard House"},
|
||||||
|
{"Granny's Potion Shop"},
|
||||||
|
{"Ganon Fight"},
|
||||||
|
{"House of Skulltula"},
|
||||||
|
{"Hyrule Field"},
|
||||||
|
{"Kakariko Village"},
|
||||||
|
{"Graveyard"},
|
||||||
|
{"Zora's River"},
|
||||||
|
{"Kokiri Forest"},
|
||||||
|
{"Sacred Forest Meadow"},
|
||||||
|
{"Lake Hylia"},
|
||||||
|
{"Zora's Domain"},
|
||||||
|
{"Zora's Fountain"},
|
||||||
|
{"Gerudo Valley"},
|
||||||
|
{"Lost Woods"},
|
||||||
|
{"Desert Colossus"},
|
||||||
|
{"Gerudo's Fortress"},
|
||||||
|
{"Haunted Wasteland"},
|
||||||
|
{"Hyrule Castle"},
|
||||||
|
{"Death Mountain Trail"},
|
||||||
|
{"Death Mountain Crater"},
|
||||||
|
{"Goron City"},
|
||||||
|
{"Lon Lon Ranch"},
|
||||||
|
{"Outside Ganon's Castle"},
|
||||||
|
//Debug Rooms
|
||||||
|
{"Test Map"},
|
||||||
|
{"Test Room"},
|
||||||
|
{"Depth Test"},
|
||||||
|
{"Stalfos Mini-Boss"},
|
||||||
|
{"Stalfos Boss"},
|
||||||
|
{"Dark Link"},
|
||||||
|
{"Castle Maze (Broken)"},
|
||||||
|
{"SRD Room"},
|
||||||
|
{"Chest Room"}
|
||||||
|
};
|
||||||
|
|
||||||
#define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f)
|
#define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f)
|
||||||
#define COLOR_RED ImVec4(1.00f, 0.00f, 0.00f, 1.00f)
|
#define COLOR_RED ImVec4(1.00f, 0.00f, 0.00f, 1.00f)
|
||||||
#define COLOR_GREEN ImVec4(0.10f, 1.00f, 0.10f, 1.00f)
|
#define COLOR_GREEN ImVec4(0.10f, 1.00f, 0.10f, 1.00f)
|
||||||
@ -23,21 +138,24 @@ extern "C" {
|
|||||||
#define COLOR_LIGHT_BLUE ImVec4(0.00f, 0.88f, 1.00f, 1.00f)
|
#define COLOR_LIGHT_BLUE ImVec4(0.00f, 0.88f, 1.00f, 1.00f)
|
||||||
#define COLOR_GREY ImVec4(0.78f, 0.78f, 0.78f, 1.00f)
|
#define COLOR_GREY ImVec4(0.78f, 0.78f, 0.78f, 1.00f)
|
||||||
|
|
||||||
char timestampDisplayName[TIMESTAMP_MAX][21] = { "" };
|
char itemTimestampDisplayName[TIMESTAMP_MAX][21] = { "" };
|
||||||
ImVec4 timestampDisplayColor[TIMESTAMP_MAX];
|
ImVec4 itemTimestampDisplayColor[TIMESTAMP_MAX];
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[21];
|
char name[40];
|
||||||
u32 time;
|
u32 time;
|
||||||
ImVec4 color;
|
ImVec4 color;
|
||||||
|
bool isRoom;
|
||||||
}TimestampInfo;
|
}TimestampInfo;
|
||||||
|
|
||||||
// Timestamps are an array of structs, each with a name, time, and color
|
// Timestamps are an array of structs, each with a name, time, and color
|
||||||
// Names and colors are set up at the bottom of this file
|
// Names and colors are set up at the bottom of this file
|
||||||
// Times are stored in gSaveContext.sohStats.timestamp
|
// Times are stored in gSaveContext.sohStats.itemTimestamp
|
||||||
TimestampInfo timestampDisplay[TIMESTAMP_MAX];
|
TimestampInfo itemTimestampDisplay[TIMESTAMP_MAX];
|
||||||
|
TimestampInfo sceneTimestampDisplay[8191];
|
||||||
|
//std::vector<TimestampInfo> sceneTimestampDisplay;
|
||||||
|
|
||||||
void DisplayTimeHHMMSS(uint32_t timeInTenthsOfSeconds, const char* text, ImVec4 color) {
|
void DisplayTimeHHMMSS(uint32_t timeInTenthsOfSeconds, std::string text, ImVec4 color) {
|
||||||
|
|
||||||
uint32_t sec = timeInTenthsOfSeconds / 10;
|
uint32_t sec = timeInTenthsOfSeconds / 10;
|
||||||
uint32_t hh = sec / 3600;
|
uint32_t hh = sec / 3600;
|
||||||
@ -47,7 +165,8 @@ void DisplayTimeHHMMSS(uint32_t timeInTenthsOfSeconds, const char* text, ImVec4
|
|||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
|
||||||
ImGui::Text(text);
|
std::string padded = fmt::format("{:<40}", text);
|
||||||
|
ImGui::Text(padded.c_str());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("%2u:%02u:%02u.%u", hh, mm, ss, ds);
|
ImGui::Text("%2u:%02u:%02u.%u", hh, mm, ss, ds);
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
@ -80,6 +199,64 @@ void DisplayStatIfNonZero(const char* text, uint32_t value) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string ResolveSceneID(int sceneID, int roomID){
|
||||||
|
std::string scene = "";
|
||||||
|
if (sceneID == SCENE_KAKUSIANA) {
|
||||||
|
switch (roomID) {
|
||||||
|
case 0:
|
||||||
|
scene = "Generic Grotto";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
scene = "Lake Hylia Scrub Grotto";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
scene = "Redead Grotto";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
scene = "Cow Grotto";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
scene = "Scrub Trio";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
scene = "Flooded Grotto";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
scene = "Scrub Duo (Upgrade)";
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
scene = "Wolfos Grotto";
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
scene = "Hyrule Castle Storms Grotto";
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
scene = "Scrub Duo";
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
scene = "Tektite Grotto";
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
scene = "Forest Stage";
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
scene = "Webbed Grotto";
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
scene = "Big Skulltula Grotto";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scene = "???";
|
||||||
|
};
|
||||||
|
} else if (sceneID == SCENE_HAKASITARELAY) {
|
||||||
|
//Only the last room of Dampe's Grave (rm 6) is considered the windmill
|
||||||
|
scene = roomID == 6 ? "Windmill" : "Dampe's Grave";
|
||||||
|
} else {
|
||||||
|
scene = sceneMappings[sceneID];
|
||||||
|
}
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
void DrawStatsTracker(bool& open) {
|
void DrawStatsTracker(bool& open) {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
CVarSetInteger("gGameplayStatsEnabled", 0);
|
CVarSetInteger("gGameplayStatsEnabled", 0);
|
||||||
@ -91,10 +268,6 @@ void DrawStatsTracker(bool& open) {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool showTimestamps = (CVarGetInteger("gGameplayStatsMode", 0) <= 1);
|
|
||||||
bool showCounts = ( (CVarGetInteger("gGameplayStatsMode", 0) == 0) || (CVarGetInteger("gGameplayStatsMode", 0) == 2) );
|
|
||||||
|
|
||||||
u32 totalTimer = GAMEPLAYSTAT_TOTAL_TIME;
|
u32 totalTimer = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
u32 enemiesDefeated = 0;
|
u32 enemiesDefeated = 0;
|
||||||
u32 ammoUsed = 0;
|
u32 ammoUsed = 0;
|
||||||
@ -117,13 +290,29 @@ void DrawStatsTracker(bool& open) {
|
|||||||
for (int i = COUNT_BUTTON_PRESSES_A; i <= COUNT_BUTTON_PRESSES_START; i++) {
|
for (int i = COUNT_BUTTON_PRESSES_A; i <= COUNT_BUTTON_PRESSES_START; i++) {
|
||||||
buttonPresses += gSaveContext.sohStats.count[i];
|
buttonPresses += gSaveContext.sohStats.count[i];
|
||||||
}
|
}
|
||||||
// Set up the array of timestamps and then sort it chronologically
|
// Set up the array of item timestamps and then sort it chronologically
|
||||||
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
||||||
strcpy(timestampDisplay[i].name, timestampDisplayName[i]);
|
strcpy(itemTimestampDisplay[i].name, itemTimestampDisplayName[i]);
|
||||||
timestampDisplay[i].time = gSaveContext.sohStats.timestamp[i];
|
itemTimestampDisplay[i].time = gSaveContext.sohStats.itemTimestamp[i];
|
||||||
timestampDisplay[i].color = timestampDisplayColor[i];
|
itemTimestampDisplay[i].color = itemTimestampDisplayColor[i];
|
||||||
}
|
}
|
||||||
SortChronological(timestampDisplay, sizeof(timestampDisplay) / sizeof(timestampDisplay[0]));
|
|
||||||
|
for (int i = 0; i < gSaveContext.sohStats.tsIdx; i++) {
|
||||||
|
std::string sceneName = ResolveSceneID(gSaveContext.sohStats.sceneTimestamps[i].scene, gSaveContext.sohStats.sceneTimestamps[i].room);
|
||||||
|
std::string name;
|
||||||
|
if (CVarGetInteger("gGameplayStatRoomBreakdown", 0) && gSaveContext.sohStats.sceneTimestamps[i].scene != SCENE_KAKUSIANA) {
|
||||||
|
name = fmt::format("{:s} Room {:d}", sceneName, gSaveContext.sohStats.sceneTimestamps[i].room);
|
||||||
|
} else {
|
||||||
|
name = sceneName;
|
||||||
|
}
|
||||||
|
strcpy(sceneTimestampDisplay[i].name, name.c_str());
|
||||||
|
sceneTimestampDisplay[i].time = CVarGetInteger("gGameplayStatRoomBreakdown", 0) ?
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[i].roomTime : gSaveContext.sohStats.sceneTimestamps[i].sceneTime;
|
||||||
|
sceneTimestampDisplay[i].color = COLOR_GREY;
|
||||||
|
sceneTimestampDisplay[i].isRoom = gSaveContext.sohStats.sceneTimestamps[i].isRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
SortChronological(itemTimestampDisplay, sizeof(itemTimestampDisplay) / sizeof(itemTimestampDisplay[0]));
|
||||||
|
|
||||||
|
|
||||||
// Begin drawing the table and showing the stats
|
// Begin drawing the table and showing the stats
|
||||||
@ -138,42 +327,32 @@ void DrawStatsTracker(bool& open) {
|
|||||||
DisplayTimeHHMMSS(gSaveContext.sohStats.playTimer / 2, "Gameplay Time: ", COLOR_WHITE);
|
DisplayTimeHHMMSS(gSaveContext.sohStats.playTimer / 2, "Gameplay Time: ", COLOR_WHITE);
|
||||||
UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading.");
|
UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading.");
|
||||||
DisplayTimeHHMMSS(gSaveContext.sohStats.pauseTimer / 3, "Pause Menu Time: ", COLOR_WHITE);
|
DisplayTimeHHMMSS(gSaveContext.sohStats.pauseTimer / 3, "Pause Menu Time: ", COLOR_WHITE);
|
||||||
|
DisplayTimeHHMMSS(gSaveContext.sohStats.sceneTimer / 2, "Time in scene: ", COLOR_LIGHT_BLUE);
|
||||||
|
UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading.");
|
||||||
|
DisplayTimeHHMMSS(gSaveContext.sohStats.roomTimer / 2, "Time in room: ", COLOR_LIGHT_BLUE);
|
||||||
|
UIWidgets::Tooltip("Timer accuracy may be affected by game performance and loading.");
|
||||||
|
ImGui::Text("Current room: %d", gSaveContext.sohStats.roomNum);
|
||||||
|
|
||||||
ImGui::PopStyleVar(1);
|
ImGui::PopStyleVar(1);
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f });
|
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f });
|
||||||
ImGui::BeginTable("gameStatsTable", (showTimestamps && showCounts) ? 2 : 1, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV);
|
if (ImGui::BeginTabBar("Stats", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
|
||||||
|
if (ImGui::BeginTabItem("Timestamps")) {
|
||||||
if (showTimestamps) {
|
|
||||||
ImGui::TableSetupColumn("Timestamps", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
||||||
}
|
|
||||||
if (showCounts) {
|
|
||||||
ImGui::TableSetupColumn("Counts", ImGuiTableColumnFlags_WidthStretch, 200.0f);
|
|
||||||
}
|
|
||||||
ImGui::TableHeadersRow();
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
|
|
||||||
if (showTimestamps) {
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
|
|
||||||
// Display chronological timestamps of items obtained and bosses defeated
|
// Display chronological timestamps of items obtained and bosses defeated
|
||||||
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
for (int i = 0; i < TIMESTAMP_MAX; i++) {
|
||||||
// To be shown, the entry must have a non-zero time and a string for its display name
|
// To be shown, the entry must have a non-zero time and a string for its display name
|
||||||
if (timestampDisplay[i].time > 0 && strnlen(timestampDisplay[i].name, 21) > 1) {
|
if (itemTimestampDisplay[i].time > 0 && strnlen(itemTimestampDisplay[i].name, 21) > 1) {
|
||||||
DisplayTimeHHMMSS(timestampDisplay[i].time, timestampDisplay[i].name, timestampDisplay[i].color);
|
DisplayTimeHHMMSS(itemTimestampDisplay[i].time, itemTimestampDisplay[i].name, itemTimestampDisplay[i].color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
if (ImGui::BeginTabItem("Counts")) {
|
||||||
if (showCounts) {
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
|
|
||||||
DisplayStat("Enemies Defeated: ", enemiesDefeated);
|
DisplayStat("Enemies Defeated: ", enemiesDefeated);
|
||||||
// Show breakdown of enemies defeated in a tree. Only show counts for enemies if they've been defeated at least once.
|
// Show breakdown of enemies defeated in a tree. Only show counts for enemies if they've been defeated at least once.
|
||||||
if (enemiesDefeated > 0) {
|
if (enemiesDefeated > 0) {
|
||||||
if (ImGui::TreeNode("Enemy Details...")) {
|
if (ImGui::TreeNode("Enemy Details...")) {
|
||||||
|
|
||||||
DisplayStatIfNonZero("Anubis: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ANUBIS]);
|
DisplayStatIfNonZero("Anubis: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ANUBIS]);
|
||||||
DisplayStatIfNonZero("Armos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARMOS]);
|
DisplayStatIfNonZero("Armos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARMOS]);
|
||||||
DisplayStatIfNonZero("Arwing: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARWING]);
|
DisplayStatIfNonZero("Arwing: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_ARWING]);
|
||||||
@ -244,7 +423,6 @@ void DrawStatsTracker(bool& open) {
|
|||||||
DisplayStatIfNonZero("Withered Deku Baba: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA]);
|
DisplayStatIfNonZero("Withered Deku Baba: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WITHERED_DEKU_BABA]);
|
||||||
DisplayStatIfNonZero("Wolfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS]);
|
DisplayStatIfNonZero("Wolfos: ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS]);
|
||||||
DisplayStatIfNonZero("Wolfos (White): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE]);
|
DisplayStatIfNonZero("Wolfos (White): ", gSaveContext.sohStats.count[COUNT_ENEMIES_DEFEATED_WOLFOS_WHITE]);
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
@ -259,7 +437,6 @@ void DrawStatsTracker(bool& open) {
|
|||||||
// Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once.
|
// Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once.
|
||||||
if (ammoUsed > 0) {
|
if (ammoUsed > 0) {
|
||||||
if (ImGui::TreeNode("Ammo Details...")) {
|
if (ImGui::TreeNode("Ammo Details...")) {
|
||||||
|
|
||||||
DisplayStatIfNonZero("Deku Sticks: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_STICK]);
|
DisplayStatIfNonZero("Deku Sticks: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_STICK]);
|
||||||
DisplayStatIfNonZero("Deku Nuts: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_NUT]);
|
DisplayStatIfNonZero("Deku Nuts: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_NUT]);
|
||||||
DisplayStatIfNonZero("Deku Seeds: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_SEED]);
|
DisplayStatIfNonZero("Deku Seeds: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_SEED]);
|
||||||
@ -267,12 +444,10 @@ void DrawStatsTracker(bool& open) {
|
|||||||
DisplayStatIfNonZero("Bombchus: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMBCHU]);
|
DisplayStatIfNonZero("Bombchus: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BOMBCHU]);
|
||||||
DisplayStatIfNonZero("Arrows: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_ARROW]);
|
DisplayStatIfNonZero("Arrows: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_ARROW]);
|
||||||
DisplayStatIfNonZero("Beans: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BEAN]);
|
DisplayStatIfNonZero("Beans: ", gSaveContext.sohStats.count[COUNT_AMMO_USED_BEAN]);
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayStat("Damage Taken: ", gSaveContext.sohStats.count[COUNT_DAMAGE_TAKEN]);
|
DisplayStat("Damage Taken: ", gSaveContext.sohStats.count[COUNT_DAMAGE_TAKEN]);
|
||||||
DisplayStat("Sword Swings: ", gSaveContext.sohStats.count[COUNT_SWORD_SWINGS]);
|
DisplayStat("Sword Swings: ", gSaveContext.sohStats.count[COUNT_SWORD_SWINGS]);
|
||||||
DisplayStat("Steps Taken: ", gSaveContext.sohStats.count[COUNT_STEPS]);
|
DisplayStat("Steps Taken: ", gSaveContext.sohStats.count[COUNT_STEPS]);
|
||||||
@ -288,12 +463,10 @@ void DrawStatsTracker(bool& open) {
|
|||||||
DisplayStat("Pauses: ", gSaveContext.sohStats.count[COUNT_PAUSES]);
|
DisplayStat("Pauses: ", gSaveContext.sohStats.count[COUNT_PAUSES]);
|
||||||
DisplayStat("Pots Smashed: ", gSaveContext.sohStats.count[COUNT_POTS_BROKEN]);
|
DisplayStat("Pots Smashed: ", gSaveContext.sohStats.count[COUNT_POTS_BROKEN]);
|
||||||
DisplayStat("Bushes Cut: ", gSaveContext.sohStats.count[COUNT_BUSHES_CUT]);
|
DisplayStat("Bushes Cut: ", gSaveContext.sohStats.count[COUNT_BUSHES_CUT]);
|
||||||
|
|
||||||
DisplayStat("Buttons Pressed: ", buttonPresses);
|
DisplayStat("Buttons Pressed: ", buttonPresses);
|
||||||
// Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once.
|
// Show breakdown of ammo used in a collapsible tree. Only show ammo types if they've been used at least once.
|
||||||
if (buttonPresses > 0) {
|
if (buttonPresses > 0) {
|
||||||
if (ImGui::TreeNode("Buttons...")) {
|
if (ImGui::TreeNode("Buttons...")) {
|
||||||
|
|
||||||
DisplayStatIfNonZero("A: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]);
|
DisplayStatIfNonZero("A: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]);
|
||||||
DisplayStatIfNonZero("B: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_B]);
|
DisplayStatIfNonZero("B: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_B]);
|
||||||
DisplayStatIfNonZero("L: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_L]);
|
DisplayStatIfNonZero("L: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_L]);
|
||||||
@ -308,23 +481,39 @@ void DrawStatsTracker(bool& open) {
|
|||||||
DisplayStatIfNonZero("D-Down: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DDOWN]);
|
DisplayStatIfNonZero("D-Down: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DDOWN]);
|
||||||
DisplayStatIfNonZero("D-Left: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DLEFT]);
|
DisplayStatIfNonZero("D-Left: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DLEFT]);
|
||||||
DisplayStatIfNonZero("Start: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]);
|
DisplayStatIfNonZero("Start: ", gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]);
|
||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
}
|
}
|
||||||
|
if (ImGui::BeginTabItem("Breakdown")) {
|
||||||
ImGui::PopStyleVar(1);
|
UIWidgets::PaddedEnhancementCheckbox("Room Breakdown", "gGameplayStatRoomBreakdown");
|
||||||
ImGui::EndTable();
|
|
||||||
|
|
||||||
const char* gameplayStatsModeOptions[3] = { "Both", "Timestamps", "Counts"};
|
|
||||||
|
|
||||||
ImGui::Text("Display Mode");
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
UIWidgets::InsertHelpHoverText("Allows a more in-depth perspective of time spent in a certain map.");
|
||||||
UIWidgets::EnhancementCombobox("gGameplayStatsMode", gameplayStatsModeOptions, 0);
|
if (gPlayState == NULL) {
|
||||||
|
ImGui::Text("Waiting for file load...");
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < gSaveContext.sohStats.tsIdx; i++) {
|
||||||
|
TimestampInfo tsInfo = sceneTimestampDisplay[i];
|
||||||
|
bool canShow = !tsInfo.isRoom || CVarGetInteger("gGameplayStatRoomBreakdown", 0);
|
||||||
|
if (tsInfo.time > 0 && strnlen(tsInfo.name, 40) > 1 && canShow) {
|
||||||
|
DisplayTimeHHMMSS(tsInfo.time, tsInfo.name, tsInfo.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string toPass;
|
||||||
|
if (CVarGetInteger("gGameplayStatRoomBreakdown", 0) && gSaveContext.sohStats.sceneNum != SCENE_KAKUSIANA) {
|
||||||
|
toPass = fmt::format("{:s} Room {:d}", ResolveSceneID(gSaveContext.sohStats.sceneNum, gSaveContext.sohStats.roomNum), gSaveContext.sohStats.roomNum);
|
||||||
|
} else {
|
||||||
|
toPass = ResolveSceneID(gSaveContext.sohStats.sceneNum, gSaveContext.sohStats.roomNum);
|
||||||
|
}
|
||||||
|
DisplayTimeHHMMSS(CURRENT_MODE_TIMER / 2, toPass.c_str(), COLOR_WHITE);
|
||||||
|
}
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
ImGui::PopStyleVar(1);
|
||||||
ImGui::Text("Note: Gameplay stats are saved to the current file and will be\nlost if you quit without saving.");
|
ImGui::Text("Note: Gameplay stats are saved to the current file and will be\nlost if you quit without saving.");
|
||||||
|
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
@ -335,94 +524,93 @@ void SetupDisplayNames() {
|
|||||||
// To add a timestamp for an item or event, add it to this list and ensure
|
// To add a timestamp for an item or event, add it to this list and ensure
|
||||||
// it has a corresponding entry in the enum (see gameplaystats.h)
|
// it has a corresponding entry in the enum (see gameplaystats.h)
|
||||||
|
|
||||||
strcpy(timestampDisplayName[ITEM_BOW], "Fairy Bow: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOW], "Fairy Bow: ");
|
||||||
strcpy(timestampDisplayName[ITEM_ARROW_FIRE], "Fire Arrows: ");
|
strcpy(itemTimestampDisplayName[ITEM_ARROW_FIRE], "Fire Arrows: ");
|
||||||
strcpy(timestampDisplayName[ITEM_DINS_FIRE], "Din's Fire: ");
|
strcpy(itemTimestampDisplayName[ITEM_DINS_FIRE], "Din's Fire: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SLINGSHOT], "Slingshot: ");
|
strcpy(itemTimestampDisplayName[ITEM_SLINGSHOT], "Slingshot: ");
|
||||||
strcpy(timestampDisplayName[ITEM_OCARINA_FAIRY], "Fairy Ocarina: ");
|
strcpy(itemTimestampDisplayName[ITEM_OCARINA_FAIRY], "Fairy Ocarina: ");
|
||||||
strcpy(timestampDisplayName[ITEM_OCARINA_TIME], "Ocarina of Time: ");
|
strcpy(itemTimestampDisplayName[ITEM_OCARINA_TIME], "Ocarina of Time: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BOMBCHU], "Bombchus: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOMBCHU], "Bombchus: ");
|
||||||
strcpy(timestampDisplayName[ITEM_HOOKSHOT], "Hookshot: ");
|
strcpy(itemTimestampDisplayName[ITEM_HOOKSHOT], "Hookshot: ");
|
||||||
strcpy(timestampDisplayName[ITEM_LONGSHOT], "Longshot: ");
|
strcpy(itemTimestampDisplayName[ITEM_LONGSHOT], "Longshot: ");
|
||||||
strcpy(timestampDisplayName[ITEM_ARROW_ICE], "Ice Arrows: ");
|
strcpy(itemTimestampDisplayName[ITEM_ARROW_ICE], "Ice Arrows: ");
|
||||||
strcpy(timestampDisplayName[ITEM_FARORES_WIND], "Farore's Wind: ");
|
strcpy(itemTimestampDisplayName[ITEM_FARORES_WIND], "Farore's Wind: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BOOMERANG], "Boomerang: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOOMERANG], "Boomerang: ");
|
||||||
strcpy(timestampDisplayName[ITEM_LENS], "Lens of Truth: ");
|
strcpy(itemTimestampDisplayName[ITEM_LENS], "Lens of Truth: ");
|
||||||
strcpy(timestampDisplayName[ITEM_HAMMER], "Megaton Hammer: ");
|
strcpy(itemTimestampDisplayName[ITEM_HAMMER], "Megaton Hammer: ");
|
||||||
strcpy(timestampDisplayName[ITEM_ARROW_LIGHT], "Light Arrows: ");
|
strcpy(itemTimestampDisplayName[ITEM_ARROW_LIGHT], "Light Arrows: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BOTTLE], "Bottle: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOTTLE], "Bottle: ");
|
||||||
strcpy(timestampDisplayName[ITEM_LETTER_ZELDA], "Zelda's Letter: ");
|
strcpy(itemTimestampDisplayName[ITEM_LETTER_ZELDA], "Zelda's Letter: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SWORD_KOKIRI], "Kokiri Sword: ");
|
strcpy(itemTimestampDisplayName[ITEM_SWORD_KOKIRI], "Kokiri Sword: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SWORD_MASTER], "Master Sword: ");
|
strcpy(itemTimestampDisplayName[ITEM_SWORD_MASTER], "Master Sword: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SWORD_BGS], "Biggoron's Sword: ");
|
strcpy(itemTimestampDisplayName[ITEM_SWORD_BGS], "Biggoron's Sword: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SHIELD_DEKU], "Deku Shield: ");
|
strcpy(itemTimestampDisplayName[ITEM_SHIELD_DEKU], "Deku Shield: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SHIELD_HYLIAN], "Hylian Shield: ");
|
strcpy(itemTimestampDisplayName[ITEM_SHIELD_HYLIAN], "Hylian Shield: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SHIELD_MIRROR], "Mirror Shield: ");
|
strcpy(itemTimestampDisplayName[ITEM_SHIELD_MIRROR], "Mirror Shield: ");
|
||||||
strcpy(timestampDisplayName[ITEM_TUNIC_GORON], "Goron Tunic: ");
|
strcpy(itemTimestampDisplayName[ITEM_TUNIC_GORON], "Goron Tunic: ");
|
||||||
strcpy(timestampDisplayName[ITEM_TUNIC_ZORA], "Zora Tunic: ");
|
strcpy(itemTimestampDisplayName[ITEM_TUNIC_ZORA], "Zora Tunic: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BOOTS_IRON], "Iron Boots: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOOTS_IRON], "Iron Boots: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BOOTS_HOVER], "Hover Boots: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOOTS_HOVER], "Hover Boots: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BOMB_BAG_20], "Bomb Bag: ");
|
strcpy(itemTimestampDisplayName[ITEM_BOMB_BAG_20], "Bomb Bag: ");
|
||||||
strcpy(timestampDisplayName[ITEM_BRACELET], "Goron's Bracelet: ");
|
strcpy(itemTimestampDisplayName[ITEM_BRACELET], "Goron's Bracelet: ");
|
||||||
strcpy(timestampDisplayName[ITEM_GAUNTLETS_SILVER], "Silver Gauntlets: ");
|
strcpy(itemTimestampDisplayName[ITEM_GAUNTLETS_SILVER], "Silver Gauntlets: ");
|
||||||
strcpy(timestampDisplayName[ITEM_GAUNTLETS_GOLD], "Gold Gauntlets: ");
|
strcpy(itemTimestampDisplayName[ITEM_GAUNTLETS_GOLD], "Gold Gauntlets: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SCALE_SILVER], "Silver Scale: ");
|
strcpy(itemTimestampDisplayName[ITEM_SCALE_SILVER], "Silver Scale: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SCALE_GOLDEN], "Gold Scale: ");
|
strcpy(itemTimestampDisplayName[ITEM_SCALE_GOLDEN], "Gold Scale: ");
|
||||||
strcpy(timestampDisplayName[ITEM_WALLET_ADULT], "Adult's Wallet: ");
|
strcpy(itemTimestampDisplayName[ITEM_WALLET_ADULT], "Adult's Wallet: ");
|
||||||
strcpy(timestampDisplayName[ITEM_WALLET_GIANT], "Giant's Wallet: ");
|
strcpy(itemTimestampDisplayName[ITEM_WALLET_GIANT], "Giant's Wallet: ");
|
||||||
strcpy(timestampDisplayName[ITEM_WEIRD_EGG], "Weird Egg: ");
|
strcpy(itemTimestampDisplayName[ITEM_WEIRD_EGG], "Weird Egg: ");
|
||||||
strcpy(timestampDisplayName[ITEM_GERUDO_CARD], "Gerudo's Card: ");
|
strcpy(itemTimestampDisplayName[ITEM_GERUDO_CARD], "Gerudo's Card: ");
|
||||||
strcpy(timestampDisplayName[ITEM_COJIRO], "Cojiro: ");
|
strcpy(itemTimestampDisplayName[ITEM_COJIRO], "Cojiro: ");
|
||||||
strcpy(timestampDisplayName[ITEM_POCKET_EGG], "Pocket Egg: ");
|
strcpy(itemTimestampDisplayName[ITEM_POCKET_EGG], "Pocket Egg: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MASK_SKULL], "Skull Mask: ");
|
strcpy(itemTimestampDisplayName[ITEM_MASK_SKULL], "Skull Mask: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MASK_SPOOKY], "Spooky Mask: ");
|
strcpy(itemTimestampDisplayName[ITEM_MASK_SPOOKY], "Spooky Mask: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MASK_KEATON], "Keaton Mask: ");
|
strcpy(itemTimestampDisplayName[ITEM_MASK_KEATON], "Keaton Mask: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MASK_BUNNY], "Bunny Hood: ");
|
strcpy(itemTimestampDisplayName[ITEM_MASK_BUNNY], "Bunny Hood: ");
|
||||||
strcpy(timestampDisplayName[ITEM_ODD_MUSHROOM], "Odd Mushroom: ");
|
strcpy(itemTimestampDisplayName[ITEM_ODD_MUSHROOM], "Odd Mushroom: ");
|
||||||
strcpy(timestampDisplayName[ITEM_ODD_POTION], "Odd Potion: ");
|
strcpy(itemTimestampDisplayName[ITEM_ODD_POTION], "Odd Potion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SAW], "Poacher's Saw: ");
|
strcpy(itemTimestampDisplayName[ITEM_SAW], "Poacher's Saw: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SWORD_BROKEN], "Broken Goron Sword: ");
|
strcpy(itemTimestampDisplayName[ITEM_SWORD_BROKEN], "Broken Goron Sword: ");
|
||||||
strcpy(timestampDisplayName[ITEM_PRESCRIPTION], "Prescription: ");
|
strcpy(itemTimestampDisplayName[ITEM_PRESCRIPTION], "Prescription: ");
|
||||||
strcpy(timestampDisplayName[ITEM_FROG], "Eyeball Frog: ");
|
strcpy(itemTimestampDisplayName[ITEM_FROG], "Eyeball Frog: ");
|
||||||
strcpy(timestampDisplayName[ITEM_EYEDROPS], "Eye Drops: ");
|
strcpy(itemTimestampDisplayName[ITEM_EYEDROPS], "Eye Drops: ");
|
||||||
strcpy(timestampDisplayName[ITEM_CLAIM_CHECK], "Claim Check: ");
|
strcpy(itemTimestampDisplayName[ITEM_CLAIM_CHECK], "Claim Check: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_MINUET], "Minuet of Forest: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_MINUET], "Minuet of Forest: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_BOLERO], "Bolero of Fire: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_BOLERO], "Bolero of Fire: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_SERENADE], "Serenade of Water: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_SERENADE], "Serenade of Water: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_REQUIEM], "Requiem of Spirit: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_REQUIEM], "Requiem of Spirit: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_NOCTURNE], "Nocturne of Shadow: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_NOCTURNE], "Nocturne of Shadow: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_PRELUDE], "Prelude of Light: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_PRELUDE], "Prelude of Light: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_LULLABY], "Zelda's Lullaby: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_LULLABY], "Zelda's Lullaby: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_EPONA], "Epona's Song: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_EPONA], "Epona's Song: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_SARIA], "Saria's Song: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_SARIA], "Saria's Song: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_SUN], "Sun's Song: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_SUN], "Sun's Song: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_TIME], "Song of Time: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_TIME], "Song of Time: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SONG_STORMS], "Song of Storms: ");
|
strcpy(itemTimestampDisplayName[ITEM_SONG_STORMS], "Song of Storms: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MEDALLION_FOREST], "Forest Medallion: ");
|
strcpy(itemTimestampDisplayName[ITEM_MEDALLION_FOREST], "Forest Medallion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MEDALLION_FIRE], "Fire Medallion: ");
|
strcpy(itemTimestampDisplayName[ITEM_MEDALLION_FIRE], "Fire Medallion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MEDALLION_WATER], "Water Medallion: ");
|
strcpy(itemTimestampDisplayName[ITEM_MEDALLION_WATER], "Water Medallion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MEDALLION_SPIRIT], "Spirit Medallion: ");
|
strcpy(itemTimestampDisplayName[ITEM_MEDALLION_SPIRIT], "Spirit Medallion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MEDALLION_SHADOW], "Shadow Medallion: ");
|
strcpy(itemTimestampDisplayName[ITEM_MEDALLION_SHADOW], "Shadow Medallion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_MEDALLION_LIGHT], "Light Medallion: ");
|
strcpy(itemTimestampDisplayName[ITEM_MEDALLION_LIGHT], "Light Medallion: ");
|
||||||
strcpy(timestampDisplayName[ITEM_KOKIRI_EMERALD], "Kokiri's Emerald: ");
|
strcpy(itemTimestampDisplayName[ITEM_KOKIRI_EMERALD], "Kokiri's Emerald: ");
|
||||||
strcpy(timestampDisplayName[ITEM_GORON_RUBY], "Goron's Ruby: ");
|
strcpy(itemTimestampDisplayName[ITEM_GORON_RUBY], "Goron's Ruby: ");
|
||||||
strcpy(timestampDisplayName[ITEM_ZORA_SAPPHIRE], "Zora's Sapphire: ");
|
strcpy(itemTimestampDisplayName[ITEM_ZORA_SAPPHIRE], "Zora's Sapphire: ");
|
||||||
strcpy(timestampDisplayName[ITEM_KEY_BOSS], "Ganon's Boss Key: ");
|
strcpy(itemTimestampDisplayName[ITEM_KEY_BOSS], "Ganon's Boss Key: ");
|
||||||
strcpy(timestampDisplayName[ITEM_SINGLE_MAGIC], "Magic: ");
|
strcpy(itemTimestampDisplayName[ITEM_SINGLE_MAGIC], "Magic: ");
|
||||||
strcpy(timestampDisplayName[ITEM_DOUBLE_DEFENSE], "Double Defense: ");
|
strcpy(itemTimestampDisplayName[ITEM_DOUBLE_DEFENSE], "Double Defense: ");
|
||||||
|
|
||||||
// Other events
|
// Other events
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_GOHMA], "Gohma Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GOHMA], "Gohma Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_KING_DODONGO], "KD Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_KING_DODONGO], "KD Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_BARINADE], "Barinade Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_BARINADE], "Barinade Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_PHANTOM_GANON], "PG Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_PHANTOM_GANON], "PG Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_VOLVAGIA], "Volvagia Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_VOLVAGIA], "Volvagia Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_MORPHA], "Morpha Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_MORPHA], "Morpha Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_BONGO_BONGO], "Bongo Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_BONGO_BONGO], "Bongo Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_TWINROVA], "Twinrova Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_TWINROVA], "Twinrova Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_GANONDORF], "Ganondorf Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANONDORF], "Ganondorf Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_DEFEAT_GANON], "Ganon Defeated: ");
|
strcpy(itemTimestampDisplayName[TIMESTAMP_DEFEAT_GANON], "Ganon Defeated: ");
|
||||||
strcpy(timestampDisplayName[TIMESTAMP_FOUND_GREG], "Greg Found: ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetupDisplayColors() {
|
void SetupDisplayColors() {
|
||||||
@ -433,42 +621,42 @@ void SetupDisplayColors() {
|
|||||||
case ITEM_SONG_SARIA:
|
case ITEM_SONG_SARIA:
|
||||||
case ITEM_MEDALLION_FOREST:
|
case ITEM_MEDALLION_FOREST:
|
||||||
case TIMESTAMP_FOUND_GREG:
|
case TIMESTAMP_FOUND_GREG:
|
||||||
timestampDisplayColor[i] = COLOR_GREEN;
|
itemTimestampDisplayColor[i] = COLOR_GREEN;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_BOLERO:
|
case ITEM_SONG_BOLERO:
|
||||||
case ITEM_GORON_RUBY:
|
case ITEM_GORON_RUBY:
|
||||||
case ITEM_MEDALLION_FIRE:
|
case ITEM_MEDALLION_FIRE:
|
||||||
timestampDisplayColor[i] = COLOR_RED;
|
itemTimestampDisplayColor[i] = COLOR_RED;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_SERENADE:
|
case ITEM_SONG_SERENADE:
|
||||||
case ITEM_ZORA_SAPPHIRE:
|
case ITEM_ZORA_SAPPHIRE:
|
||||||
case ITEM_MEDALLION_WATER:
|
case ITEM_MEDALLION_WATER:
|
||||||
timestampDisplayColor[i] = COLOR_BLUE;
|
itemTimestampDisplayColor[i] = COLOR_BLUE;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_LULLABY:
|
case ITEM_SONG_LULLABY:
|
||||||
case ITEM_SONG_NOCTURNE:
|
case ITEM_SONG_NOCTURNE:
|
||||||
case ITEM_MEDALLION_SHADOW:
|
case ITEM_MEDALLION_SHADOW:
|
||||||
timestampDisplayColor[i] = COLOR_PURPLE;
|
itemTimestampDisplayColor[i] = COLOR_PURPLE;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_EPONA:
|
case ITEM_SONG_EPONA:
|
||||||
case ITEM_SONG_REQUIEM:
|
case ITEM_SONG_REQUIEM:
|
||||||
case ITEM_MEDALLION_SPIRIT:
|
case ITEM_MEDALLION_SPIRIT:
|
||||||
timestampDisplayColor[i] = COLOR_ORANGE;
|
itemTimestampDisplayColor[i] = COLOR_ORANGE;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_SUN:
|
case ITEM_SONG_SUN:
|
||||||
case ITEM_SONG_PRELUDE:
|
case ITEM_SONG_PRELUDE:
|
||||||
case ITEM_MEDALLION_LIGHT:
|
case ITEM_MEDALLION_LIGHT:
|
||||||
case ITEM_ARROW_LIGHT:
|
case ITEM_ARROW_LIGHT:
|
||||||
timestampDisplayColor[i] = COLOR_YELLOW;
|
itemTimestampDisplayColor[i] = COLOR_YELLOW;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_STORMS:
|
case ITEM_SONG_STORMS:
|
||||||
timestampDisplayColor[i] = COLOR_GREY;
|
itemTimestampDisplayColor[i] = COLOR_GREY;
|
||||||
break;
|
break;
|
||||||
case ITEM_SONG_TIME:
|
case ITEM_SONG_TIME:
|
||||||
timestampDisplayColor[i] = COLOR_LIGHT_BLUE;
|
itemTimestampDisplayColor[i] = COLOR_LIGHT_BLUE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
timestampDisplayColor[i] = COLOR_WHITE;
|
itemTimestampDisplayColor[i] = COLOR_WHITE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
// I.E. game time counts frames at 20fps/2, pause time counts frames at 30fps/3
|
// I.E. game time counts frames at 20fps/2, pause time counts frames at 30fps/3
|
||||||
// Frame counts in z_play.c and z_kaleido_scope_call.c
|
// Frame counts in z_play.c and z_kaleido_scope_call.c
|
||||||
#define GAMEPLAYSTAT_TOTAL_TIME (gSaveContext.sohStats.playTimer / 2 + gSaveContext.sohStats.pauseTimer / 3)
|
#define GAMEPLAYSTAT_TOTAL_TIME (gSaveContext.sohStats.playTimer / 2 + gSaveContext.sohStats.pauseTimer / 3)
|
||||||
|
#define CURRENT_MODE_TIMER (CVarGetInteger("gGameplayStatRoomBreakdown", 0) ?\
|
||||||
|
gSaveContext.sohStats.roomTimer :\
|
||||||
|
gSaveContext.sohStats.sceneTimer)
|
||||||
|
|
||||||
void InitStatTracker();
|
void InitStatTracker();
|
||||||
|
|
||||||
|
@ -489,9 +489,17 @@ void SaveManager::InitFileNormal() {
|
|||||||
}
|
}
|
||||||
gSaveContext.sohStats.playTimer = 0;
|
gSaveContext.sohStats.playTimer = 0;
|
||||||
gSaveContext.sohStats.pauseTimer = 0;
|
gSaveContext.sohStats.pauseTimer = 0;
|
||||||
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.timestamp); timestamp++) {
|
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp); timestamp++) {
|
||||||
gSaveContext.sohStats.timestamp[timestamp] = 0;
|
gSaveContext.sohStats.itemTimestamp[timestamp] = 0;
|
||||||
}
|
}
|
||||||
|
for (int timestamp = 0; timestamp < ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps); timestamp++) {
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[timestamp].sceneTime = 0;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[timestamp].roomTime = 0;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[timestamp].scene = 254;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[timestamp].room = 254;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[timestamp].isRoom = 0;
|
||||||
|
}
|
||||||
|
gSaveContext.sohStats.tsIdx = 0;
|
||||||
for (int count = 0; count < ARRAY_COUNT(gSaveContext.sohStats.count); count++) {
|
for (int count = 0; count < ARRAY_COUNT(gSaveContext.sohStats.count); count++) {
|
||||||
gSaveContext.sohStats.count[count] = 0;
|
gSaveContext.sohStats.count[count] = 0;
|
||||||
}
|
}
|
||||||
@ -1069,8 +1077,8 @@ void SaveManager::LoadBaseVersion2() {
|
|||||||
});
|
});
|
||||||
SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer);
|
SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer);
|
||||||
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
||||||
SaveManager::Instance->LoadArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.timestamp), [](size_t i) {
|
SaveManager::Instance->LoadArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) {
|
||||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.timestamp[i]);
|
SaveManager::Instance->LoadData("", gSaveContext.sohStats.itemTimestamp[i]);
|
||||||
});
|
});
|
||||||
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
||||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
|
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
|
||||||
@ -1283,9 +1291,20 @@ void SaveManager::LoadBaseVersion3() {
|
|||||||
});
|
});
|
||||||
SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer);
|
SaveManager::Instance->LoadData("playTimer", gSaveContext.sohStats.playTimer);
|
||||||
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
SaveManager::Instance->LoadData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
||||||
SaveManager::Instance->LoadArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.timestamp), [](size_t i) {
|
SaveManager::Instance->LoadArray("itemTimestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) {
|
||||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.timestamp[i]);
|
SaveManager::Instance->LoadData("", gSaveContext.sohStats.itemTimestamp[i]);
|
||||||
});
|
});
|
||||||
|
SaveManager::Instance->LoadArray("sceneTimestamps", ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps), [](size_t i) {
|
||||||
|
SaveManager::Instance->LoadStruct("", [&i]() {
|
||||||
|
SaveManager::Instance->LoadData("scene", gSaveContext.sohStats.sceneTimestamps[i].scene);
|
||||||
|
SaveManager::Instance->LoadData("room", gSaveContext.sohStats.sceneTimestamps[i].room);
|
||||||
|
SaveManager::Instance->LoadData("sceneTime", gSaveContext.sohStats.sceneTimestamps[i].sceneTime);
|
||||||
|
SaveManager::Instance->LoadData("roomTime", gSaveContext.sohStats.sceneTimestamps[i].roomTime);
|
||||||
|
SaveManager::Instance->LoadData("isRoom", gSaveContext.sohStats.sceneTimestamps[i].isRoom);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
SaveManager::Instance->LoadData("tsIdx", gSaveContext.sohStats.tsIdx);
|
||||||
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
SaveManager::Instance->LoadArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
||||||
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
|
SaveManager::Instance->LoadData("", gSaveContext.sohStats.count[i]);
|
||||||
});
|
});
|
||||||
@ -1481,9 +1500,19 @@ void SaveManager::SaveBase() {
|
|||||||
});
|
});
|
||||||
SaveManager::Instance->SaveData("playTimer", gSaveContext.sohStats.playTimer);
|
SaveManager::Instance->SaveData("playTimer", gSaveContext.sohStats.playTimer);
|
||||||
SaveManager::Instance->SaveData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
SaveManager::Instance->SaveData("pauseTimer", gSaveContext.sohStats.pauseTimer);
|
||||||
SaveManager::Instance->SaveArray("timestamps", ARRAY_COUNT(gSaveContext.sohStats.timestamp), [](size_t i) {
|
SaveManager::Instance->SaveArray("itemTimestamps", ARRAY_COUNT(gSaveContext.sohStats.itemTimestamp), [](size_t i) {
|
||||||
SaveManager::Instance->SaveData("", gSaveContext.sohStats.timestamp[i]);
|
SaveManager::Instance->SaveData("", gSaveContext.sohStats.itemTimestamp[i]);
|
||||||
});
|
});
|
||||||
|
SaveManager::Instance->SaveArray("sceneTimestamps", ARRAY_COUNT(gSaveContext.sohStats.sceneTimestamps), [](size_t i) {
|
||||||
|
SaveManager::Instance->SaveStruct("", [&i]() {
|
||||||
|
SaveManager::Instance->SaveData("scene", gSaveContext.sohStats.sceneTimestamps[i].scene);
|
||||||
|
SaveManager::Instance->SaveData("room", gSaveContext.sohStats.sceneTimestamps[i].room);
|
||||||
|
SaveManager::Instance->SaveData("sceneTime", gSaveContext.sohStats.sceneTimestamps[i].sceneTime);
|
||||||
|
SaveManager::Instance->SaveData("roomTime", gSaveContext.sohStats.sceneTimestamps[i].roomTime);
|
||||||
|
SaveManager::Instance->SaveData("isRoom", gSaveContext.sohStats.sceneTimestamps[i].isRoom);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
SaveManager::Instance->SaveData("tsIdx", gSaveContext.sohStats.tsIdx);
|
||||||
SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
SaveManager::Instance->SaveArray("counts", ARRAY_COUNT(gSaveContext.sohStats.count), [](size_t i) {
|
||||||
SaveManager::Instance->SaveData("", gSaveContext.sohStats.count[i]);
|
SaveManager::Instance->SaveData("", gSaveContext.sohStats.count[i]);
|
||||||
});
|
});
|
||||||
|
@ -1625,7 +1625,7 @@ void func_80084BF4(PlayState* play, u16 flag) {
|
|||||||
void GameplayStats_SetTimestamp(PlayState* play, u8 item) {
|
void GameplayStats_SetTimestamp(PlayState* play, u8 item) {
|
||||||
|
|
||||||
// If we already have a timestamp for this item, do nothing
|
// If we already have a timestamp for this item, do nothing
|
||||||
if (gSaveContext.sohStats.timestamp[item] != 0){
|
if (gSaveContext.sohStats.itemTimestamp[item] != 0){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Use ITEM_KEY_BOSS only for Ganon's boss key - not any other boss keys
|
// Use ITEM_KEY_BOSS only for Ganon's boss key - not any other boss keys
|
||||||
@ -1644,20 +1644,20 @@ void GameplayStats_SetTimestamp(PlayState* play, u8 item) {
|
|||||||
|
|
||||||
// Count any bottled item as a bottle
|
// Count any bottled item as a bottle
|
||||||
if (item >= ITEM_BOTTLE && item <= ITEM_POE) {
|
if (item >= ITEM_BOTTLE && item <= ITEM_POE) {
|
||||||
if (gSaveContext.sohStats.timestamp[ITEM_BOTTLE] == 0) {
|
if (gSaveContext.sohStats.itemTimestamp[ITEM_BOTTLE] == 0) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_BOTTLE] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_BOTTLE] = time;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Count any bombchu pack as bombchus
|
// Count any bombchu pack as bombchus
|
||||||
if (item == ITEM_BOMBCHU || (item >= ITEM_BOMBCHUS_5 && item <= ITEM_BOMBCHUS_20)) {
|
if (item == ITEM_BOMBCHU || (item >= ITEM_BOMBCHUS_5 && item <= ITEM_BOMBCHUS_20)) {
|
||||||
if (gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] == 0) {
|
if (gSaveContext.sohStats.itemTimestamp[ITEM_BOMBCHU] == 0) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_BOMBCHU] = time;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gSaveContext.sohStats.timestamp[item] = time;
|
gSaveContext.sohStats.itemTimestamp[item] = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gameplay stat tracking: Update time the item was acquired
|
// Gameplay stat tracking: Update time the item was acquired
|
||||||
@ -1673,28 +1673,28 @@ void Randomizer_GameplayStats_SetTimestamp(uint16_t item) {
|
|||||||
|
|
||||||
// Use ITEM_KEY_BOSS to timestamp Ganon's boss key
|
// Use ITEM_KEY_BOSS to timestamp Ganon's boss key
|
||||||
if (item == RG_GANONS_CASTLE_BOSS_KEY) {
|
if (item == RG_GANONS_CASTLE_BOSS_KEY) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_KEY_BOSS] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_KEY_BOSS] = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count any bottled item as a bottle
|
// Count any bottled item as a bottle
|
||||||
if (item >= RG_EMPTY_BOTTLE && item <= RG_BOTTLE_WITH_BIG_POE) {
|
if (item >= RG_EMPTY_BOTTLE && item <= RG_BOTTLE_WITH_BIG_POE) {
|
||||||
if (gSaveContext.sohStats.timestamp[ITEM_BOTTLE] == 0) {
|
if (gSaveContext.sohStats.itemTimestamp[ITEM_BOTTLE] == 0) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_BOTTLE] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_BOTTLE] = time;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Count any bombchu pack as bombchus
|
// Count any bombchu pack as bombchus
|
||||||
if (item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_DROP) {
|
if (item >= RG_BOMBCHU_5 && item <= RG_BOMBCHU_DROP) {
|
||||||
if (gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] = 0) {
|
if (gSaveContext.sohStats.itemTimestamp[ITEM_BOMBCHU] = 0) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_BOMBCHU] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_BOMBCHU] = time;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (item == RG_MAGIC_SINGLE) {
|
if (item == RG_MAGIC_SINGLE) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_SINGLE_MAGIC] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_SINGLE_MAGIC] = time;
|
||||||
}
|
}
|
||||||
if (item == RG_DOUBLE_DEFENSE) {
|
if (item == RG_DOUBLE_DEFENSE) {
|
||||||
gSaveContext.sohStats.timestamp[ITEM_DOUBLE_DEFENSE] = time;
|
gSaveContext.sohStats.itemTimestamp[ITEM_DOUBLE_DEFENSE] = time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2525,7 +2525,7 @@ u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
|
|||||||
if (item == RG_GREG_RUPEE) {
|
if (item == RG_GREG_RUPEE) {
|
||||||
Rupees_ChangeBy(1);
|
Rupees_ChangeBy(1);
|
||||||
Flags_SetRandomizerInf(RAND_INF_GREG_FOUND);
|
Flags_SetRandomizerInf(RAND_INF_GREG_FOUND);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_FOUND_GREG] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_FOUND_GREG] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
return Return_Item_Entry(giEntry, RG_NONE);
|
return Return_Item_Entry(giEntry, RG_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,6 +647,33 @@ void Play_Init(GameState* thisx) {
|
|||||||
gSaveContext.natureAmbienceId = play->sequenceCtx.natureAmbienceId;
|
gSaveContext.natureAmbienceId = play->sequenceCtx.natureAmbienceId;
|
||||||
func_8002DF18(play, GET_PLAYER(play));
|
func_8002DF18(play, GET_PLAYER(play));
|
||||||
AnimationContext_Update(play, &play->animationCtx);
|
AnimationContext_Update(play, &play->animationCtx);
|
||||||
|
|
||||||
|
if (gSaveContext.sohStats.sceneNum != gPlayState->sceneNum) {
|
||||||
|
u16 idx = gSaveContext.sohStats.tsIdx;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].sceneTime = gSaveContext.sohStats.sceneTimer / 2;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].roomTime = gSaveContext.sohStats.roomTimer / 2;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].scene = gSaveContext.sohStats.sceneNum;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].room = gSaveContext.sohStats.roomNum;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].isRoom =
|
||||||
|
gPlayState->sceneNum == gSaveContext.sohStats.sceneTimestamps[idx].scene &&
|
||||||
|
gPlayState->roomCtx.curRoom.num != gSaveContext.sohStats.sceneTimestamps[idx].room;
|
||||||
|
gSaveContext.sohStats.tsIdx++;
|
||||||
|
gSaveContext.sohStats.sceneTimer = 0;
|
||||||
|
gSaveContext.sohStats.roomTimer = 0;
|
||||||
|
} else if (gSaveContext.sohStats.roomNum != gPlayState->roomCtx.curRoom.num) {
|
||||||
|
u16 idx = gSaveContext.sohStats.tsIdx;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].roomTime = gSaveContext.sohStats.roomTimer / 2;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].scene = gSaveContext.sohStats.sceneNum;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].room = gSaveContext.sohStats.roomNum;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].isRoom =
|
||||||
|
gPlayState->sceneNum == gSaveContext.sohStats.sceneTimestamps[idx].scene &&
|
||||||
|
gPlayState->roomCtx.curRoom.num != gSaveContext.sohStats.sceneTimestamps[idx].room;
|
||||||
|
gSaveContext.sohStats.tsIdx++;
|
||||||
|
gSaveContext.sohStats.roomTimer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gSaveContext.sohStats.sceneNum = gPlayState->sceneNum;
|
||||||
|
gSaveContext.sohStats.roomNum = gPlayState->roomCtx.curRoom.num;
|
||||||
gSaveContext.respawnFlag = 0;
|
gSaveContext.respawnFlag = 0;
|
||||||
#if 0
|
#if 0
|
||||||
if (dREG(95) != 0) {
|
if (dREG(95) != 0) {
|
||||||
@ -1085,6 +1112,8 @@ void Play_Update(PlayState* play) {
|
|||||||
// Gameplay stat tracking
|
// Gameplay stat tracking
|
||||||
if (!gSaveContext.sohStats.gameComplete) {
|
if (!gSaveContext.sohStats.gameComplete) {
|
||||||
gSaveContext.sohStats.playTimer++;
|
gSaveContext.sohStats.playTimer++;
|
||||||
|
gSaveContext.sohStats.sceneTimer++;
|
||||||
|
gSaveContext.sohStats.roomTimer++;
|
||||||
|
|
||||||
if (CVarGetInteger("gMMBunnyHood", 0) && Player_GetMask(play) == PLAYER_MASK_BUNNY) {
|
if (CVarGetInteger("gMMBunnyHood", 0) && Player_GetMask(play) == PLAYER_MASK_BUNNY) {
|
||||||
gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD]++;
|
gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD]++;
|
||||||
|
@ -641,11 +641,21 @@ void Room_Draw(PlayState* play, Room* room, u32 flags) {
|
|||||||
void func_80097534(PlayState* play, RoomContext* roomCtx) {
|
void func_80097534(PlayState* play, RoomContext* roomCtx) {
|
||||||
roomCtx->prevRoom.num = -1;
|
roomCtx->prevRoom.num = -1;
|
||||||
roomCtx->prevRoom.segment = NULL;
|
roomCtx->prevRoom.segment = NULL;
|
||||||
func_80031B14(play, &play->actorCtx);
|
func_80031B14(play, &play->actorCtx); //kills all actors without room num set to -1
|
||||||
Actor_SpawnTransitionActors(play, &play->actorCtx);
|
Actor_SpawnTransitionActors(play, &play->actorCtx);
|
||||||
Map_InitRoomData(play, roomCtx->curRoom.num);
|
Map_InitRoomData(play, roomCtx->curRoom.num);
|
||||||
if (!((play->sceneNum >= SCENE_SPOT00) && (play->sceneNum <= SCENE_SPOT20))) {
|
if (!((play->sceneNum >= SCENE_SPOT00) && (play->sceneNum <= SCENE_SPOT20))) {
|
||||||
Map_SavePlayerInitialInfo(play);
|
Map_SavePlayerInitialInfo(play);
|
||||||
}
|
}
|
||||||
Audio_SetEnvReverb(play->roomCtx.curRoom.echo);
|
Audio_SetEnvReverb(play->roomCtx.curRoom.echo);
|
||||||
|
u8 idx = gSaveContext.sohStats.tsIdx;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].scene = gSaveContext.sohStats.sceneNum;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].room = gSaveContext.sohStats.roomNum;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].roomTime = gSaveContext.sohStats.roomTimer / 2;
|
||||||
|
gSaveContext.sohStats.sceneTimestamps[idx].isRoom =
|
||||||
|
gPlayState->sceneNum == gSaveContext.sohStats.sceneTimestamps[idx].scene &&
|
||||||
|
gPlayState->roomCtx.curRoom.num != gSaveContext.sohStats.sceneTimestamps[idx].room;
|
||||||
|
gSaveContext.sohStats.tsIdx++;
|
||||||
|
gSaveContext.sohStats.roomNum = roomCtx->curRoom.num;
|
||||||
|
gSaveContext.sohStats.roomTimer = 0;
|
||||||
}
|
}
|
||||||
|
@ -1330,7 +1330,7 @@ void BossDodongo_DeathCutscene(BossDodongo* this, PlayState* play) {
|
|||||||
this->cameraAt.x = camera->at.x;
|
this->cameraAt.x = camera->at.x;
|
||||||
this->cameraAt.y = camera->at.y;
|
this->cameraAt.y = camera->at.y;
|
||||||
this->cameraAt.z = camera->at.z;
|
this->cameraAt.z = camera->at.z;
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_KING_DODONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f;
|
tempSin = Math_SinS(this->actor.shape.rot.y - 0x1388) * 150.0f;
|
||||||
|
@ -893,7 +893,7 @@ void BossFd2_CollisionCheck(BossFd2* this, PlayState* play) {
|
|||||||
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
||||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD);
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_VALVAISA_DEAD);
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_VOLVAGIA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
} else if (damage) {
|
} else if (damage) {
|
||||||
BossFd2_SetupDamaged(this, play);
|
BossFd2_SetupDamaged(this, play);
|
||||||
this->work[FD2_DAMAGE_FLASH_TIMER] = 10;
|
this->work[FD2_DAMAGE_FLASH_TIMER] = 10;
|
||||||
|
@ -2802,7 +2802,7 @@ void BossGanon_UpdateDamage(BossGanon* this, PlayState* play) {
|
|||||||
func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE);
|
func_80078914(&sZeroVec, NA_SE_EN_LAST_DAMAGE);
|
||||||
Audio_QueueSeqCmd(0x100100FF);
|
Audio_QueueSeqCmd(0x100100FF);
|
||||||
this->screenFlashTimer = 4;
|
this->screenFlashTimer = 4;
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANONDORF] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
} else {
|
} else {
|
||||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2);
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_DAMAGE2);
|
||||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY);
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_GANON_CUTBODY);
|
||||||
|
@ -1680,7 +1680,7 @@ void func_8090120C(BossGanon2* this, PlayState* play) {
|
|||||||
(player->swordState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) {
|
(player->swordState != 0) && (player->heldItemAction == PLAYER_IA_SWORD_MASTER)) {
|
||||||
func_80064520(play, &play->csCtx);
|
func_80064520(play, &play->csCtx);
|
||||||
gSaveContext.sohStats.gameComplete = true;
|
gSaveContext.sohStats.gameComplete = true;
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
this->unk_39E = Play_CreateSubCamera(play);
|
this->unk_39E = Play_CreateSubCamera(play);
|
||||||
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
|
Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_WAIT);
|
||||||
Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE);
|
Play_ChangeCameraStatus(play, this->unk_39E, CAM_STAT_ACTIVE);
|
||||||
|
@ -1280,7 +1280,7 @@ void BossGanondrof_CollisionCheck(BossGanondrof* this, PlayState* play) {
|
|||||||
if ((s8)this->actor.colChkInfo.health <= 0) {
|
if ((s8)this->actor.colChkInfo.health <= 0) {
|
||||||
BossGanondrof_SetupDeath(this, play);
|
BossGanondrof_SetupDeath(this, play);
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_PHANTOM_GANON] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1841,7 +1841,7 @@ void BossGoma_UpdateHit(BossGoma* this, PlayState* play) {
|
|||||||
} else {
|
} else {
|
||||||
BossGoma_SetupDefeated(this, play);
|
BossGoma_SetupDefeated(this, play);
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GOHMA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->invincibilityFrames = 10;
|
this->invincibilityFrames = 10;
|
||||||
|
@ -1786,7 +1786,7 @@ void BossMo_CoreCollisionCheck(BossMo* this, PlayState* play) {
|
|||||||
if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) ||
|
if (((sMorphaTent1->csCamera == 0) && (sMorphaTent2 == NULL)) ||
|
||||||
((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) {
|
((sMorphaTent1->csCamera == 0) && (sMorphaTent2 != NULL) && (sMorphaTent2->csCamera == 0))) {
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_MORPHA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
Audio_QueueSeqCmd(0x1 << 28 | SEQ_PLAYER_BGM_MAIN << 24 | 0x100FF);
|
||||||
this->csState = MO_DEATH_START;
|
this->csState = MO_DEATH_START;
|
||||||
sMorphaTent1->drawActor = false;
|
sMorphaTent1->drawActor = false;
|
||||||
|
@ -2557,7 +2557,7 @@ void BossSst_HeadCollisionCheck(BossSst* this, PlayState* play) {
|
|||||||
if (Actor_ApplyDamage(&this->actor) == 0) {
|
if (Actor_ApplyDamage(&this->actor) == 0) {
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
BossSst_HeadSetupDeath(this, play);
|
BossSst_HeadSetupDeath(this, play);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BONGO_BONGO] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
} else {
|
} else {
|
||||||
BossSst_HeadSetupDamage(this);
|
BossSst_HeadSetupDamage(this);
|
||||||
}
|
}
|
||||||
|
@ -5285,7 +5285,7 @@ void BossTw_TwinrovaDamage(BossTw* this, PlayState* play, u8 damage) {
|
|||||||
BossTw_TwinrovaSetupDeathCS(this, play);
|
BossTw_TwinrovaSetupDeathCS(this, play);
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD);
|
Audio_PlayActorSound2(&this->actor, NA_SE_EN_TWINROBA_YOUNG_DEAD);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_TWINROVA] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1394,7 +1394,7 @@ void BossVa_BodyPhase4(BossVa* this, PlayState* play) {
|
|||||||
if (sFightPhase >= PHASE_DEATH) {
|
if (sFightPhase >= PHASE_DEATH) {
|
||||||
BossVa_SetupBodyDeath(this, play);
|
BossVa_SetupBodyDeath(this, play);
|
||||||
Enemy_StartFinishingBlow(play, &this->actor);
|
Enemy_StartFinishingBlow(play, &this->actor);
|
||||||
gSaveContext.sohStats.timestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME;
|
gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_BARINADE] = GAMEPLAYSTAT_TOTAL_TIME;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->actor.speedXZ = -10.0f;
|
this->actor.speedXZ = -10.0f;
|
||||||
|
Loading…
Reference in New Issue
Block a user