2022-11-22 20:04:40 -05:00
# include "gameplaystats.h"
2023-06-03 15:27:45 -04:00
# include "gameplaystatswindow.h"
2022-11-22 20:04:40 -05:00
2023-05-11 15:02:04 -04:00
# include "soh/SaveManager.h"
2023-05-12 16:23:50 -04:00
# include "functions.h"
2023-05-11 15:02:04 -04:00
# include "macros.h"
2024-11-19 11:44:44 -05:00
# include "soh/cvar_prefixes.h"
2022-11-22 20:04:40 -05:00
# include "../UIWidgets.hpp"
2023-10-27 15:18:56 -04:00
# include "soh/util.h"
2022-11-22 20:04:40 -05:00
2023-04-03 00:06:55 -04:00
# include <vector>
2022-11-22 20:04:40 -05:00
# include <string>
2023-01-17 00:17:49 -05:00
# include <libultraship/bridge.h>
2023-06-03 15:27:45 -04:00
# include <libultraship/libultraship.h>
2023-06-09 17:22:25 -04:00
# include "soh/Enhancements/enhancementTypes.h"
2024-04-25 20:31:28 -04:00
# include "soh/OTRGlobals.h"
2022-11-22 20:04:40 -05:00
extern " C " {
# include <z64.h>
# include "variables.h"
2023-04-03 00:06:55 -04:00
extern PlayState * gPlayState ;
2023-05-21 18:35:56 -04:00
uint64_t GetUnixTimestamp ( ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
const char * const 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 " ,
2023-04-03 00:06:55 -04:00
//Debug Rooms
2023-05-21 18:35:56 -04:00
" Test Map " ,
" Test Room " ,
" Depth Test " ,
" Stalfos Mini-Boss " ,
" Stalfos Boss " ,
" Dark Link " ,
" Castle Maze (Broken) " ,
" SRD Room " ,
" Chest Room " ,
} ;
const char * const countMappings [ ] = {
" Anubis: " ,
" Armos: " ,
" Arwing: " ,
" Bari: " ,
" Biri: " ,
" Beamos: " ,
" Big Octo: " ,
" Bubble (Blue): " ,
" Bubble (Green): " ,
" Bubble (Red): " ,
" Bubble (White): " ,
" Business Scrub: " ,
" Dark Link: " ,
" Dead Hand: " ,
" Deku Baba: " ,
" Deku Baba (Big): " ,
" Deku Scrub: " ,
" Dinolfos: " ,
" Dodongo: " ,
" Dodongo (Baby): " ,
" Door Mimic: " ,
" Flare Dancer: " ,
" Floormaster: " ,
" Flying Floor Tile: " ,
" Flying Pot: " ,
" Freezard: " ,
" Gerudo Thief: " ,
" Gibdo: " ,
" Gohma Larva: " ,
" Guay: " ,
" Iron Knuckle: " ,
" Iron Knuckle (Nab): " ,
" Keese: " ,
" Keese (Fire): " ,
" Keese (Ice): " ,
" Leever: " ,
" Leever (Big): " ,
" Like-Like: " ,
" Lizalfos: " ,
" Mad Scrub: " ,
" Moblin: " ,
" Moblin (Club): " ,
" Octorok: " ,
" Parasitic Tentacle: " ,
" Peahat: " ,
" Peahat Larva: " ,
" Poe: " ,
" Poe (Big): " ,
" Poe (Composer): " ,
" Poe Sisters: " ,
" Redead: " ,
" Shabom: " ,
" Shellblade: " ,
" Skull Kid: " ,
" Skulltula: " ,
" Skulltula (Big): " ,
" Skulltula (Gold): " ,
" Skullwalltula: " ,
" Spike: " ,
" Stalchild: " ,
" Stalfos: " ,
" Stinger: " ,
" Tailpasaran: " ,
" Tektite (Blue): " ,
" Tektite (Red): " ,
" Torch Slug: " ,
" Wallmaster: " ,
" Withered Deku Baba: " ,
" Wolfos: " ,
" Wolfos (White): " ,
" Deku Sticks: " ,
" Deku Nuts: " ,
" Bombs: " ,
" Arrows: " ,
" Deku Seeds: " ,
" Bombchus: " ,
" Beans: " ,
" A: " ,
" B: " ,
" L: " ,
" R: " ,
" Z: " ,
" C-Up: " ,
" C-Right: " ,
" C-Down: " ,
" C-Left: " ,
" D-Up: " ,
" D-Right: " ,
" D-Down: " ,
" D-Left: " ,
" Start: " ,
2023-04-03 00:06:55 -04:00
} ;
2022-11-22 20:04:40 -05:00
# 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_GREEN ImVec4(0.10f, 1.00f, 0.10f, 1.00f)
# define COLOR_BLUE ImVec4(0.00f, 0.33f, 1.00f, 1.00f)
# define COLOR_PURPLE ImVec4(0.54f, 0.19f, 0.89f, 1.00f)
# define COLOR_YELLOW ImVec4(1.00f, 1.00f, 0.00f, 1.00f)
# define COLOR_ORANGE ImVec4(1.00f, 0.67f, 0.11f, 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)
2023-04-03 00:06:55 -04:00
char itemTimestampDisplayName [ TIMESTAMP_MAX ] [ 21 ] = { " " } ;
ImVec4 itemTimestampDisplayColor [ TIMESTAMP_MAX ] ;
2022-11-22 20:04:40 -05:00
typedef struct {
2023-04-03 00:06:55 -04:00
char name [ 40 ] ;
2022-11-22 20:04:40 -05:00
u32 time ;
ImVec4 color ;
2023-04-03 00:06:55 -04:00
bool isRoom ;
2022-11-22 20:04:40 -05:00
} TimestampInfo ;
// 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
2023-04-03 00:06:55 -04:00
// Times are stored in gSaveContext.sohStats.itemTimestamp
TimestampInfo itemTimestampDisplay [ TIMESTAMP_MAX ] ;
TimestampInfo sceneTimestampDisplay [ 8191 ] ;
//std::vector<TimestampInfo> sceneTimestampDisplay;
2022-11-22 20:04:40 -05:00
2023-05-21 18:35:56 -04:00
std : : string formatTimestampGameplayStat ( uint32_t value ) {
uint32_t sec = value / 10 ;
2022-11-22 20:04:40 -05:00
uint32_t hh = sec / 3600 ;
uint32_t mm = ( sec - hh * 3600 ) / 60 ;
uint32_t ss = sec - hh * 3600 - mm * 60 ;
2023-05-21 18:35:56 -04:00
uint32_t ds = value % 10 ;
return fmt : : format ( " {}:{:0>2}:{:0>2}.{} " , hh , mm , ss , ds ) ;
}
2022-11-22 20:04:40 -05:00
2023-05-21 18:35:56 -04:00
std : : string formatIntGameplayStat ( uint32_t value ) {
return fmt : : format ( " {} " , value ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
std : : string formatHexGameplayStat ( uint32_t value ) {
return fmt : : format ( " {:#x} ({:d}) " , value , value ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
std : : string formatHexOnlyGameplayStat ( uint32_t value ) {
return fmt : : format ( " {:#x} " , value , value ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-30 18:57:45 -04:00
extern " C " char * GameplayStats_GetCurrentTime ( ) {
std : : string timeString = formatTimestampGameplayStat ( GAMEPLAYSTAT_TOTAL_TIME ) . c_str ( ) ;
2023-08-30 13:02:07 -04:00
const size_t stringLength = timeString . length ( ) ;
char * timeChar = ( char * ) malloc ( stringLength + 1 ) ; // We need to use malloc so we can free this from a C file.
2023-05-30 18:57:45 -04:00
strcpy ( timeChar , timeString . c_str ( ) ) ;
return timeChar ;
}
2023-05-11 15:02:04 -04:00
void LoadStatsVersion1 ( ) {
2023-10-27 15:18:56 -04:00
SaveManager : : Instance - > LoadCharArray ( " buildVersion " , gSaveContext . sohStats . buildVersion ,
ARRAY_COUNT ( gSaveContext . sohStats . buildVersion ) ) ;
2023-05-11 15:02:04 -04:00
SaveManager : : Instance - > LoadData ( " buildVersionMajor " , gSaveContext . sohStats . buildVersionMajor ) ;
SaveManager : : Instance - > LoadData ( " buildVersionMinor " , gSaveContext . sohStats . buildVersionMinor ) ;
SaveManager : : Instance - > LoadData ( " buildVersionPatch " , gSaveContext . sohStats . buildVersionPatch ) ;
SaveManager : : Instance - > LoadData ( " heartPieces " , gSaveContext . sohStats . heartPieces ) ;
SaveManager : : Instance - > LoadData ( " heartContainers " , gSaveContext . sohStats . heartContainers ) ;
SaveManager : : Instance - > LoadArray ( " dungeonKeys " , ARRAY_COUNT ( gSaveContext . sohStats . dungeonKeys ) , [ ] ( size_t i ) {
SaveManager : : Instance - > LoadData ( " " , gSaveContext . sohStats . dungeonKeys [ i ] ) ;
} ) ;
2023-05-24 16:00:35 -04:00
SaveManager : : Instance - > LoadData ( " rtaTiming " , gSaveContext . sohStats . rtaTiming ) ;
SaveManager : : Instance - > LoadData ( " fileCreatedAt " , gSaveContext . sohStats . fileCreatedAt ) ;
2023-05-11 15:02:04 -04:00
SaveManager : : Instance - > LoadData ( " playTimer " , gSaveContext . sohStats . playTimer ) ;
SaveManager : : Instance - > LoadData ( " pauseTimer " , gSaveContext . sohStats . pauseTimer ) ;
2023-05-13 21:29:05 -04:00
SaveManager : : Instance - > LoadArray ( " itemTimestamps " , ARRAY_COUNT ( gSaveContext . sohStats . itemTimestamp ) , [ ] ( size_t i ) {
SaveManager : : Instance - > LoadData ( " " , gSaveContext . sohStats . itemTimestamp [ i ] ) ;
} ) ;
2023-05-25 19:57:27 -04:00
SaveManager : : Instance - > LoadArray ( " sceneTimestamps " , ARRAY_COUNT ( gSaveContext . sohStats . sceneTimestamps ) , [ & ] ( size_t i ) {
SaveManager : : Instance - > LoadStruct ( " " , [ & ] ( ) {
int scene , room , sceneTime , roomTime , isRoom ;
SaveManager : : Instance - > LoadData ( " scene " , scene ) ;
SaveManager : : Instance - > LoadData ( " room " , room ) ;
SaveManager : : Instance - > LoadData ( " sceneTime " , sceneTime ) ;
SaveManager : : Instance - > LoadData ( " roomTime " , roomTime ) ;
SaveManager : : Instance - > LoadData ( " isRoom " , isRoom ) ;
if ( scene = = 0 & & room = = 0 & & sceneTime = = 0 & & roomTime = = 0 & & isRoom = = 0 ) {
return ;
}
gSaveContext . sohStats . sceneTimestamps [ i ] . scene = scene ;
gSaveContext . sohStats . sceneTimestamps [ i ] . room = room ;
gSaveContext . sohStats . sceneTimestamps [ i ] . sceneTime = sceneTime ;
gSaveContext . sohStats . sceneTimestamps [ i ] . roomTime = roomTime ;
gSaveContext . sohStats . sceneTimestamps [ i ] . isRoom = isRoom ;
2023-05-11 15:02:04 -04:00
} ) ;
2023-05-13 21:29:05 -04:00
} ) ;
2023-05-11 15:02:04 -04:00
SaveManager : : Instance - > LoadData ( " tsIdx " , gSaveContext . sohStats . tsIdx ) ;
SaveManager : : Instance - > LoadArray ( " counts " , ARRAY_COUNT ( gSaveContext . sohStats . count ) , [ ] ( size_t i ) {
SaveManager : : Instance - > LoadData ( " " , gSaveContext . sohStats . count [ i ] ) ;
} ) ;
2023-05-13 21:29:05 -04:00
SaveManager : : Instance - > LoadArray ( " scenesDiscovered " , ARRAY_COUNT ( gSaveContext . sohStats . scenesDiscovered ) , [ ] ( size_t i ) {
SaveManager : : Instance - > LoadData ( " " , gSaveContext . sohStats . scenesDiscovered [ i ] ) ;
} ) ;
SaveManager : : Instance - > LoadArray ( " entrancesDiscovered " , ARRAY_COUNT ( gSaveContext . sohStats . entrancesDiscovered ) , [ ] ( size_t i ) {
SaveManager : : Instance - > LoadData ( " " , gSaveContext . sohStats . entrancesDiscovered [ i ] ) ;
} ) ;
2023-05-11 15:02:04 -04:00
}
2023-06-14 23:39:14 -04:00
void SaveStats ( SaveContext * saveContext , int sectionID , bool fullSave ) {
2023-05-24 16:00:35 -04:00
SaveManager : : Instance - > SaveData ( " buildVersion " , saveContext - > sohStats . buildVersion ) ;
SaveManager : : Instance - > SaveData ( " buildVersionMajor " , saveContext - > sohStats . buildVersionMajor ) ;
SaveManager : : Instance - > SaveData ( " buildVersionMinor " , saveContext - > sohStats . buildVersionMinor ) ;
SaveManager : : Instance - > SaveData ( " buildVersionPatch " , saveContext - > sohStats . buildVersionPatch ) ;
SaveManager : : Instance - > SaveData ( " heartPieces " , saveContext - > sohStats . heartPieces ) ;
SaveManager : : Instance - > SaveData ( " heartContainers " , saveContext - > sohStats . heartContainers ) ;
SaveManager : : Instance - > SaveArray ( " dungeonKeys " , ARRAY_COUNT ( saveContext - > sohStats . dungeonKeys ) , [ & ] ( size_t i ) {
SaveManager : : Instance - > SaveData ( " " , saveContext - > sohStats . dungeonKeys [ i ] ) ;
} ) ;
SaveManager : : Instance - > SaveData ( " rtaTiming " , saveContext - > sohStats . rtaTiming ) ;
SaveManager : : Instance - > SaveData ( " fileCreatedAt " , saveContext - > sohStats . fileCreatedAt ) ;
SaveManager : : Instance - > SaveData ( " playTimer " , saveContext - > sohStats . playTimer ) ;
SaveManager : : Instance - > SaveData ( " pauseTimer " , saveContext - > sohStats . pauseTimer ) ;
SaveManager : : Instance - > SaveArray ( " itemTimestamps " , ARRAY_COUNT ( saveContext - > sohStats . itemTimestamp ) , [ & ] ( size_t i ) {
SaveManager : : Instance - > SaveData ( " " , saveContext - > sohStats . itemTimestamp [ i ] ) ;
} ) ;
SaveManager : : Instance - > SaveArray ( " sceneTimestamps " , ARRAY_COUNT ( saveContext - > sohStats . sceneTimestamps ) , [ & ] ( size_t i ) {
2023-05-25 19:57:27 -04:00
if ( saveContext - > sohStats . sceneTimestamps [ i ] . scene ! = 254 & & saveContext - > sohStats . sceneTimestamps [ i ] . room ! = 254 ) {
SaveManager : : Instance - > SaveStruct ( " " , [ & ] ( ) {
SaveManager : : Instance - > SaveData ( " scene " , saveContext - > sohStats . sceneTimestamps [ i ] . scene ) ;
SaveManager : : Instance - > SaveData ( " room " , saveContext - > sohStats . sceneTimestamps [ i ] . room ) ;
SaveManager : : Instance - > SaveData ( " sceneTime " , saveContext - > sohStats . sceneTimestamps [ i ] . sceneTime ) ;
SaveManager : : Instance - > SaveData ( " roomTime " , saveContext - > sohStats . sceneTimestamps [ i ] . roomTime ) ;
SaveManager : : Instance - > SaveData ( " isRoom " , saveContext - > sohStats . sceneTimestamps [ i ] . isRoom ) ;
} ) ;
}
2023-05-24 16:00:35 -04:00
} ) ;
SaveManager : : Instance - > SaveData ( " tsIdx " , saveContext - > sohStats . tsIdx ) ;
SaveManager : : Instance - > SaveArray ( " counts " , ARRAY_COUNT ( saveContext - > sohStats . count ) , [ & ] ( size_t i ) {
SaveManager : : Instance - > SaveData ( " " , saveContext - > sohStats . count [ i ] ) ;
} ) ;
SaveManager : : Instance - > SaveArray ( " scenesDiscovered " , ARRAY_COUNT ( saveContext - > sohStats . scenesDiscovered ) , [ & ] ( size_t i ) {
SaveManager : : Instance - > SaveData ( " " , saveContext - > sohStats . scenesDiscovered [ i ] ) ;
} ) ;
SaveManager : : Instance - > SaveArray ( " entrancesDiscovered " , ARRAY_COUNT ( saveContext - > sohStats . entrancesDiscovered ) , [ & ] ( size_t i ) {
SaveManager : : Instance - > SaveData ( " " , saveContext - > sohStats . entrancesDiscovered [ i ] ) ;
} ) ;
2022-11-22 20:04:40 -05:00
}
2023-08-30 13:02:07 -04:00
void GameplayStatsRow ( const char * label , const std : : string & value , ImVec4 color = COLOR_WHITE ) {
2023-05-21 18:35:56 -04:00
ImGui : : PushStyleColor ( ImGuiCol_Text , color ) ;
ImGui : : TableNextRow ( ) ;
ImGui : : TableNextColumn ( ) ;
2023-08-30 13:02:07 -04:00
ImGui : : Text ( " %s " , label ) ;
2023-05-21 18:35:56 -04:00
ImGui : : SameLine ( ImGui : : GetContentRegionAvail ( ) . x - ( ImGui : : CalcTextSize ( value . c_str ( ) ) . x - 8.0f ) ) ;
ImGui : : Text ( " %s " , value . c_str ( ) ) ;
ImGui : : PopStyleColor ( ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
bool compareTimestampInfoByTime ( const TimestampInfo & a , const TimestampInfo & b ) {
2024-05-05 15:14:55 -04:00
return CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.ReverseTimestamps " ) , 0 ) ? a . time > b . time : a . time < b . time ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
const char * ResolveSceneID ( int sceneID , int roomID ) {
2023-09-01 12:46:19 -04:00
if ( sceneID = = SCENE_GROTTOS ) {
2023-04-03 00:06:55 -04:00
switch ( roomID ) {
case 0 :
2023-05-21 18:35:56 -04:00
return " Generic Grotto " ;
2023-04-03 00:06:55 -04:00
case 1 :
2023-05-21 18:35:56 -04:00
return " Lake Hylia Scrub Grotto " ;
2023-04-03 00:06:55 -04:00
case 2 :
2023-05-21 18:35:56 -04:00
return " Redead Grotto " ;
2023-04-03 00:06:55 -04:00
case 3 :
2023-05-21 18:35:56 -04:00
return " Cow Grotto " ;
2023-04-03 00:06:55 -04:00
case 4 :
2023-05-21 18:35:56 -04:00
return " Scrub Trio " ;
2023-04-03 00:06:55 -04:00
case 5 :
2023-05-21 18:35:56 -04:00
return " Flooded Grotto " ;
2023-04-03 00:06:55 -04:00
case 6 :
2023-05-21 18:35:56 -04:00
return " Scrub Duo (Upgrade) " ;
2023-04-03 00:06:55 -04:00
case 7 :
2023-05-21 18:35:56 -04:00
return " Wolfos Grotto " ;
2023-04-03 00:06:55 -04:00
case 8 :
2023-05-21 18:35:56 -04:00
return " Hyrule Castle Storms Grotto " ;
2023-04-03 00:06:55 -04:00
case 9 :
2023-05-21 18:35:56 -04:00
return " Scrub Duo " ;
2023-04-03 00:06:55 -04:00
case 10 :
2023-05-21 18:35:56 -04:00
return " Tektite Grotto " ;
2023-04-03 00:06:55 -04:00
case 11 :
2023-05-21 18:35:56 -04:00
return " Forest Stage " ;
2023-04-03 00:06:55 -04:00
case 12 :
2023-05-21 18:35:56 -04:00
return " Webbed Grotto " ;
2023-04-03 00:06:55 -04:00
case 13 :
2023-05-21 18:35:56 -04:00
return " Big Skulltula Grotto " ;
2023-04-03 00:06:55 -04:00
} ;
2023-09-01 12:46:19 -04:00
} else if ( sceneID = = SCENE_WINDMILL_AND_DAMPES_GRAVE ) {
2023-04-03 00:06:55 -04:00
//Only the last room of Dampe's Grave (rm 6) is considered the windmill
2023-05-21 18:35:56 -04:00
return roomID = = 6 ? " Windmill " : " Dampe's Grave " ;
2023-04-06 23:09:25 -04:00
} else if ( sceneID < SCENE_ID_MAX ) {
2023-05-21 18:35:56 -04:00
return sceneMappings [ sceneID ] ;
}
return " ??? " ;
}
void DrawGameplayStatsHeader ( ) {
ImGui : : PushStyleVar ( ImGuiStyleVar_CellPadding , { 4.0f , 4.0f } ) ;
ImGui : : BeginTable ( " gameplayStatsHeader " , 1 , ImGuiTableFlags_BordersOuter ) ;
ImGui : : TableSetupColumn ( " stat " , ImGuiTableColumnFlags_WidthStretch ) ;
2024-08-23 10:17:45 -04:00
//if tag is empty (not a release build)
if ( gGitCommitTag [ 0 ] = = 0 ) {
GameplayStatsRow ( " Git Branch: " , ( char * ) gGitBranch ) ;
GameplayStatsRow ( " Git Commit Hash: " , ( char * ) gGitCommitHash ) ;
} else {
GameplayStatsRow ( " Build Version: " , ( char * ) gBuildVersion ) ;
}
2023-05-21 18:35:56 -04:00
if ( gSaveContext . sohStats . rtaTiming ) {
GameplayStatsRow ( " Total Time (RTA): " , formatTimestampGameplayStat ( GAMEPLAYSTAT_TOTAL_TIME ) , gSaveContext . sohStats . gameComplete ? COLOR_GREEN : COLOR_WHITE ) ;
2023-04-06 23:09:25 -04:00
} else {
2023-05-21 18:35:56 -04:00
GameplayStatsRow ( " Total Game Time: " , formatTimestampGameplayStat ( GAMEPLAYSTAT_TOTAL_TIME ) , gSaveContext . sohStats . gameComplete ? COLOR_GREEN : COLOR_WHITE ) ;
}
2024-05-05 15:14:55 -04:00
if ( CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.ShowAdditionalTimers " ) , 0 ) ) { // !Only display total game time
2023-05-21 18:35:56 -04:00
GameplayStatsRow ( " Gameplay Time: " , formatTimestampGameplayStat ( gSaveContext . sohStats . playTimer / 2 ) , COLOR_GREY ) ;
GameplayStatsRow ( " Pause Menu Time: " , formatTimestampGameplayStat ( gSaveContext . sohStats . pauseTimer / 3 ) , COLOR_GREY ) ;
GameplayStatsRow ( " Time in scene: " , formatTimestampGameplayStat ( gSaveContext . sohStats . sceneTimer / 2 ) , COLOR_LIGHT_BLUE ) ;
GameplayStatsRow ( " Time in room: " , formatTimestampGameplayStat ( gSaveContext . sohStats . roomTimer / 2 ) , COLOR_LIGHT_BLUE ) ;
2023-04-03 00:06:55 -04:00
}
2024-05-05 15:14:55 -04:00
if ( gPlayState ! = NULL & & CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.ShowDebugInfo " ) , 0 ) ) { // && display debug info
2023-05-21 18:35:56 -04:00
GameplayStatsRow ( " play->sceneNum: " , formatHexGameplayStat ( gPlayState - > sceneNum ) , COLOR_YELLOW ) ;
GameplayStatsRow ( " gSaveContext.entranceIndex: " , formatHexGameplayStat ( gSaveContext . entranceIndex ) , COLOR_YELLOW ) ;
GameplayStatsRow ( " gSaveContext.cutsceneIndex: " , formatHexOnlyGameplayStat ( gSaveContext . cutsceneIndex ) , COLOR_YELLOW ) ;
GameplayStatsRow ( " play->roomCtx.curRoom.num: " , formatIntGameplayStat ( gPlayState - > roomCtx . curRoom . num ) , COLOR_YELLOW ) ;
2023-04-03 00:06:55 -04:00
}
2023-05-21 18:35:56 -04:00
ImGui : : EndTable ( ) ;
ImGui : : PopStyleVar ( 1 ) ;
2023-04-03 00:06:55 -04:00
}
2023-05-21 18:35:56 -04:00
void DrawGameplayStatsTimestampsTab ( ) {
// Set up the array of item timestamps and then sort it chronologically
for ( int i = 0 ; i < TIMESTAMP_MAX ; i + + ) {
strcpy ( itemTimestampDisplay [ i ] . name , itemTimestampDisplayName [ i ] ) ;
itemTimestampDisplay [ i ] . time = gSaveContext . sohStats . itemTimestamp [ i ] ;
itemTimestampDisplay [ i ] . color = itemTimestampDisplayColor [ i ] ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
std : : sort ( itemTimestampDisplay , itemTimestampDisplay + TIMESTAMP_MAX , compareTimestampInfoByTime ) ;
ImGui : : PushStyleVar ( ImGuiStyleVar_CellPadding , { 4.0f , 4.0f } ) ;
ImGui : : BeginTable ( " gameplayStatsTimestamps " , 1 , ImGuiTableFlags_BordersOuter ) ;
ImGui : : TableSetupColumn ( " stat " , ImGuiTableColumnFlags_WidthStretch ) ;
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
if ( itemTimestampDisplay [ i ] . time > 0 & & strnlen ( itemTimestampDisplay [ i ] . name , 21 ) > 1 ) {
GameplayStatsRow ( itemTimestampDisplay [ i ] . name , formatTimestampGameplayStat ( itemTimestampDisplay [ i ] . time ) , itemTimestampDisplay [ i ] . color ) ;
}
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
ImGui : : EndTable ( ) ;
ImGui : : PopStyleVar ( 1 ) ;
}
void DrawGameplayStatsCountsTab ( ) {
2022-11-22 20:04:40 -05:00
u32 enemiesDefeated = 0 ;
u32 ammoUsed = 0 ;
u32 buttonPresses = 0 ;
// Sum of all enemies defeated
for ( int i = COUNT_ENEMIES_DEFEATED_ANUBIS ; i < = COUNT_ENEMIES_DEFEATED_WOLFOS ; i + + ) {
if ( i = = COUNT_ENEMIES_DEFEATED_FLOORMASTER ) {
// Special case: You must kill 3 mini Floormasters for it count as one defeated Floormaster
enemiesDefeated + = gSaveContext . sohStats . count [ i ] / 3 ;
} else {
enemiesDefeated + = gSaveContext . sohStats . count [ i ] ;
}
}
// Sum of all ammo used
for ( int i = COUNT_AMMO_USED_STICK ; i < = COUNT_AMMO_USED_BEAN ; i + + ) {
ammoUsed + = gSaveContext . sohStats . count [ i ] ;
}
// Sum of all button presses
for ( int i = COUNT_BUTTON_PRESSES_A ; i < = COUNT_BUTTON_PRESSES_START ; i + + ) {
buttonPresses + = gSaveContext . sohStats . count [ i ] ;
}
2023-05-21 18:35:56 -04:00
ImGui : : PushStyleVar ( ImGuiStyleVar_CellPadding , { 4.0f , 4.0f } ) ;
ImGui : : BeginTable ( " gameplayStatsCounts " , 1 , ImGuiTableFlags_BordersOuter ) ;
ImGui : : TableSetupColumn ( " stat " , ImGuiTableColumnFlags_WidthStretch ) ;
GameplayStatsRow ( " Enemies Defeated: " , formatIntGameplayStat ( enemiesDefeated ) ) ;
if ( enemiesDefeated > 0 ) {
ImGui : : TableNextRow ( ) ; ImGui : : TableNextColumn ( ) ;
if ( ImGui : : TreeNodeEx ( " Enemy Details... " , ImGuiTreeNodeFlags_NoTreePushOnOpen ) ) {
for ( int i = COUNT_ENEMIES_DEFEATED_ANUBIS ; i < = COUNT_ENEMIES_DEFEATED_WOLFOS ; i + + ) {
if ( i = = COUNT_ENEMIES_DEFEATED_FLOORMASTER ) {
GameplayStatsRow ( countMappings [ i ] , formatIntGameplayStat ( gSaveContext . sohStats . count [ i ] / 3 ) ) ;
} else {
GameplayStatsRow ( countMappings [ i ] , formatIntGameplayStat ( gSaveContext . sohStats . count [ i ] ) ) ;
}
}
}
2023-04-03 00:06:55 -04:00
}
2023-05-21 18:35:56 -04:00
GameplayStatsRow ( " Rupees Collected: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_RUPEES_COLLECTED ] ) ) ;
UIWidgets : : Tooltip ( " Includes rupees collected with a full wallet. " ) ;
GameplayStatsRow ( " Rupees Spent: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_RUPEES_SPENT ] ) ) ;
GameplayStatsRow ( " Chests Opened: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_CHESTS_OPENED ] ) ) ;
GameplayStatsRow ( " Ammo Used: " , formatIntGameplayStat ( ammoUsed ) ) ;
if ( ammoUsed > 0 ) {
ImGui : : TableNextRow ( ) ; ImGui : : TableNextColumn ( ) ;
if ( ImGui : : TreeNodeEx ( " Ammo Details... " , ImGuiTreeNodeFlags_NoTreePushOnOpen ) ) {
for ( int i = COUNT_AMMO_USED_STICK ; i < = COUNT_AMMO_USED_BEAN ; i + + ) {
GameplayStatsRow ( countMappings [ i ] , formatIntGameplayStat ( gSaveContext . sohStats . count [ i ] ) ) ;
}
}
}
GameplayStatsRow ( " Damage Taken: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_DAMAGE_TAKEN ] ) ) ;
GameplayStatsRow ( " Sword Swings: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_SWORD_SWINGS ] ) ) ;
GameplayStatsRow ( " Steps Taken: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_STEPS ] ) ) ;
// If using MM Bunny Hood enhancement, show how long it's been equipped (not counting pause time)
2024-04-25 20:31:28 -04:00
if ( CVarGetInteger ( CVAR_ENHANCEMENT ( " MMBunnyHood " ) , BUNNY_HOOD_VANILLA ) ! = BUNNY_HOOD_VANILLA | | gSaveContext . sohStats . count [ COUNT_TIME_BUNNY_HOOD ] > 0 ) {
2023-05-21 18:35:56 -04:00
GameplayStatsRow ( " Bunny Hood Time: " , formatTimestampGameplayStat ( gSaveContext . sohStats . count [ COUNT_TIME_BUNNY_HOOD ] / 2 ) ) ;
}
GameplayStatsRow ( " Rolls: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_ROLLS ] ) ) ;
GameplayStatsRow ( " Bonks: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_BONKS ] ) ) ;
GameplayStatsRow ( " Sidehops: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_SIDEHOPS ] ) ) ;
GameplayStatsRow ( " Backflips: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_BACKFLIPS ] ) ) ;
GameplayStatsRow ( " Ice Traps: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_ICE_TRAPS ] ) ) ;
GameplayStatsRow ( " Pauses: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_PAUSES ] ) ) ;
GameplayStatsRow ( " Pots Smashed: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_POTS_BROKEN ] ) ) ;
GameplayStatsRow ( " Bushes Cut: " , formatIntGameplayStat ( gSaveContext . sohStats . count [ COUNT_BUSHES_CUT ] ) ) ;
GameplayStatsRow ( " Buttons Pressed: " , formatIntGameplayStat ( buttonPresses ) ) ;
if ( buttonPresses > 0 ) {
ImGui : : TableNextRow ( ) ; ImGui : : TableNextColumn ( ) ;
if ( ImGui : : TreeNodeEx ( " Buttons... " , ImGuiTreeNodeFlags_NoTreePushOnOpen ) ) {
for ( int i = COUNT_BUTTON_PRESSES_A ; i < = COUNT_BUTTON_PRESSES_START ; i + + ) {
GameplayStatsRow ( countMappings [ i ] , formatIntGameplayStat ( gSaveContext . sohStats . count [ i ] ) ) ;
}
}
}
ImGui : : EndTable ( ) ;
ImGui : : PopStyleVar ( 1 ) ;
}
void DrawGameplayStatsBreakdownTab ( ) {
2023-04-03 00:06:55 -04:00
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 ;
2024-05-05 15:14:55 -04:00
if ( CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.RoomBreakdown " ) , 0 ) & & gSaveContext . sohStats . sceneTimestamps [ i ] . scene ! = SCENE_GROTTOS ) {
2023-04-03 00:06:55 -04:00
name = fmt : : format ( " {:s} Room {:d} " , sceneName , gSaveContext . sohStats . sceneTimestamps [ i ] . room ) ;
} else {
name = sceneName ;
}
strcpy ( sceneTimestampDisplay [ i ] . name , name . c_str ( ) ) ;
2024-05-05 15:14:55 -04:00
sceneTimestampDisplay [ i ] . time = CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.RoomBreakdown " ) , 0 ) ?
2023-04-03 00:06:55 -04:00
gSaveContext . sohStats . sceneTimestamps [ i ] . roomTime : gSaveContext . sohStats . sceneTimestamps [ i ] . sceneTime ;
sceneTimestampDisplay [ i ] . color = COLOR_GREY ;
sceneTimestampDisplay [ i ] . isRoom = gSaveContext . sohStats . sceneTimestamps [ i ] . isRoom ;
2022-11-22 20:04:40 -05:00
}
2023-04-03 00:06:55 -04:00
2023-05-21 18:35:56 -04:00
ImGui : : PushStyleVar ( ImGuiStyleVar_CellPadding , { 4.0f , 4.0f } ) ;
ImGui : : BeginTable ( " gameplayStatsCounts " , 1 , ImGuiTableFlags_BordersOuter ) ;
ImGui : : TableSetupColumn ( " stat " , ImGuiTableColumnFlags_WidthStretch ) ;
for ( int i = 0 ; i < gSaveContext . sohStats . tsIdx ; i + + ) {
TimestampInfo tsInfo = sceneTimestampDisplay [ i ] ;
2024-05-05 15:14:55 -04:00
bool canShow = ! tsInfo . isRoom | | CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.RoomBreakdown " ) , 0 ) ;
2023-05-21 18:35:56 -04:00
if ( tsInfo . time > 0 & & strnlen ( tsInfo . name , 40 ) > 1 & & canShow ) {
GameplayStatsRow ( tsInfo . name , formatTimestampGameplayStat ( tsInfo . time ) , tsInfo . color ) ;
}
}
std : : string toPass ;
2024-05-05 15:14:55 -04:00
if ( CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.RoomBreakdown " ) , 0 ) & & gSaveContext . sohStats . sceneNum ! = SCENE_GROTTOS ) {
2023-05-21 18:35:56 -04:00
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 ) ;
}
GameplayStatsRow ( toPass . c_str ( ) , formatTimestampGameplayStat ( CURRENT_MODE_TIMER / 2 ) ) ;
ImGui : : EndTable ( ) ;
ImGui : : PopStyleVar ( 1 ) ;
}
2022-11-22 20:04:40 -05:00
2023-05-21 18:35:56 -04:00
void DrawGameplayStatsOptionsTab ( ) {
2024-05-05 15:14:55 -04:00
UIWidgets : : PaddedEnhancementCheckbox ( " Show in-game total timer " , CVAR_ENHANCEMENT ( " GameplayStats.ShowIngameTimer " ) , true , false ) ;
2023-05-30 18:57:45 -04:00
UIWidgets : : InsertHelpHoverText ( " Keep track of the timer as an in-game HUD element. The position of the timer can be changed in the Cosmetics Editor. " ) ;
2024-05-05 15:14:55 -04:00
UIWidgets : : PaddedEnhancementCheckbox ( " Show latest timestamps on top " , CVAR_ENHANCEMENT ( " GameplayStats.ReverseTimestamps " ) , true , false ) ;
UIWidgets : : PaddedEnhancementCheckbox ( " Room Breakdown " , CVAR_ENHANCEMENT ( " GameplayStats.RoomBreakdown " ) , true , false ) ;
2023-05-21 18:35:56 -04:00
ImGui : : SameLine ( ) ;
UIWidgets : : InsertHelpHoverText ( " Allows a more in-depth perspective of time spent in a certain map. " ) ;
2024-05-05 15:14:55 -04:00
UIWidgets : : PaddedEnhancementCheckbox ( " RTA Timing on new files " , CVAR_ENHANCEMENT ( " GameplayStats.RTATiming " ) , true , false ) ;
2023-05-21 18:35:56 -04:00
ImGui : : SameLine ( ) ;
UIWidgets : : InsertHelpHoverText (
" Timestamps are relative to starting timestamp rather than in game time, usually necessary for races/speedruns. \n \n "
" Starting timestamp is on first non-c-up input after intro cutscene. \n \n "
" NOTE: THIS NEEDS TO BE SET BEFORE CREATING A FILE TO TAKE EFFECT "
) ;
2024-05-05 15:14:55 -04:00
UIWidgets : : PaddedEnhancementCheckbox ( " Show additional detail timers " , CVAR_ENHANCEMENT ( " GameplayStats.ShowAdditionalTimers " ) , true , false ) ;
UIWidgets : : PaddedEnhancementCheckbox ( " Show Debug Info " , CVAR_ENHANCEMENT ( " GameplayStats.ShowDebugInfo " ) ) ;
2023-05-21 18:35:56 -04:00
}
2022-11-22 20:04:40 -05:00
2023-06-03 15:27:45 -04:00
void GameplayStatsWindow : : DrawElement ( ) {
2023-05-21 18:35:56 -04:00
DrawGameplayStatsHeader ( ) ;
2022-11-22 20:04:40 -05:00
2023-04-03 00:06:55 -04:00
if ( ImGui : : BeginTabBar ( " Stats " , ImGuiTabBarFlags_NoCloseWithMiddleMouseButton ) ) {
if ( ImGui : : BeginTabItem ( " Timestamps " ) ) {
2023-05-21 18:35:56 -04:00
DrawGameplayStatsTimestampsTab ( ) ;
2023-04-03 00:06:55 -04:00
ImGui : : EndTabItem ( ) ;
2023-01-25 17:43:50 -05:00
}
2023-04-03 00:06:55 -04:00
if ( ImGui : : BeginTabItem ( " Counts " ) ) {
2023-05-21 18:35:56 -04:00
DrawGameplayStatsCountsTab ( ) ;
ImGui : : EndTabItem ( ) ;
2022-11-22 20:04:40 -05:00
}
2023-04-03 00:06:55 -04:00
if ( ImGui : : BeginTabItem ( " Breakdown " ) ) {
2023-05-21 18:35:56 -04:00
DrawGameplayStatsBreakdownTab ( ) ;
2023-04-03 00:06:55 -04:00
ImGui : : EndTabItem ( ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
if ( ImGui : : BeginTabItem ( " Options " ) ) {
DrawGameplayStatsOptionsTab ( ) ;
ImGui : : EndTabItem ( ) ;
}
ImGui : : EndTabBar ( ) ;
2022-11-22 20:04:40 -05:00
}
2023-05-21 18:35:56 -04:00
2022-11-22 20:04:40 -05:00
ImGui : : Text ( " Note: Gameplay stats are saved to the current file and will be \n lost if you quit without saving. " ) ;
}
2023-05-24 16:00:35 -04:00
void InitStats ( bool isDebug ) {
gSaveContext . sohStats . heartPieces = isDebug ? 8 : 0 ;
gSaveContext . sohStats . heartContainers = isDebug ? 8 : 0 ;
for ( int dungeon = 0 ; dungeon < ARRAY_COUNT ( gSaveContext . sohStats . dungeonKeys ) ; dungeon + + ) {
gSaveContext . sohStats . dungeonKeys [ dungeon ] = isDebug ? 8 : 0 ;
}
2024-05-05 15:14:55 -04:00
gSaveContext . sohStats . rtaTiming = CVarGetInteger ( CVAR_ENHANCEMENT ( " GameplayStats.RTATiming " ) , 0 ) ;
2023-05-24 16:00:35 -04:00
gSaveContext . sohStats . fileCreatedAt = 0 ;
gSaveContext . sohStats . playTimer = 0 ;
gSaveContext . sohStats . pauseTimer = 0 ;
for ( int timestamp = 0 ; timestamp < ARRAY_COUNT ( gSaveContext . sohStats . itemTimestamp ) ; timestamp + + ) {
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 + + ) {
gSaveContext . sohStats . count [ count ] = 0 ;
}
gSaveContext . sohStats . gameComplete = false ;
for ( int scenesIdx = 0 ; scenesIdx < ARRAY_COUNT ( gSaveContext . sohStats . scenesDiscovered ) ; scenesIdx + + ) {
gSaveContext . sohStats . scenesDiscovered [ scenesIdx ] = 0 ;
}
for ( int entrancesIdx = 0 ; entrancesIdx < ARRAY_COUNT ( gSaveContext . sohStats . entrancesDiscovered ) ; entrancesIdx + + ) {
gSaveContext . sohStats . entrancesDiscovered [ entrancesIdx ] = 0 ;
}
2023-10-27 15:18:56 -04:00
SohUtils : : CopyStringToCharArray ( gSaveContext . sohStats . buildVersion , std : : string ( ( char * ) gBuildVersion ) ,
ARRAY_COUNT ( gSaveContext . sohStats . buildVersion ) ) ;
2023-05-24 16:00:35 -04:00
gSaveContext . sohStats . buildVersionMajor = gBuildVersionMajor ;
gSaveContext . sohStats . buildVersionMinor = gBuildVersionMinor ;
gSaveContext . sohStats . buildVersionPatch = gBuildVersionPatch ;
}
2022-11-22 20:04:40 -05:00
// Entries listed here will have a timestamp shown in the stat window
void SetupDisplayNames ( ) {
// 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)
2023-04-03 00:06:55 -04:00
strcpy ( itemTimestampDisplayName [ ITEM_BOW ] , " Fairy Bow: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_ARROW_FIRE ] , " Fire Arrows: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_DINS_FIRE ] , " Din's Fire: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SLINGSHOT ] , " Slingshot: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_OCARINA_FAIRY ] , " Fairy Ocarina: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_OCARINA_TIME ] , " Ocarina of Time: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BOMBCHU ] , " Bombchus: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_HOOKSHOT ] , " Hookshot: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_LONGSHOT ] , " Longshot: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_ARROW_ICE ] , " Ice Arrows: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_FARORES_WIND ] , " Farore's Wind: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BOOMERANG ] , " Boomerang: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_LENS ] , " Lens of Truth: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_HAMMER ] , " Megaton Hammer: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_ARROW_LIGHT ] , " Light Arrows: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BOTTLE ] , " Bottle: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_LETTER_ZELDA ] , " Zelda's Letter: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SWORD_KOKIRI ] , " Kokiri Sword: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SWORD_MASTER ] , " Master Sword: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SWORD_BGS ] , " Biggoron's Sword: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SHIELD_DEKU ] , " Deku Shield: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SHIELD_HYLIAN ] , " Hylian Shield: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SHIELD_MIRROR ] , " Mirror Shield: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_TUNIC_GORON ] , " Goron Tunic: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_TUNIC_ZORA ] , " Zora Tunic: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BOOTS_IRON ] , " Iron Boots: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BOOTS_HOVER ] , " Hover Boots: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BOMB_BAG_20 ] , " Bomb Bag: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_BRACELET ] , " Goron's Bracelet: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_GAUNTLETS_SILVER ] , " Silver Gauntlets: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_GAUNTLETS_GOLD ] , " Gold Gauntlets: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SCALE_SILVER ] , " Silver Scale: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SCALE_GOLDEN ] , " Gold Scale: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_WALLET_ADULT ] , " Adult's Wallet: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_WALLET_GIANT ] , " Giant's Wallet: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_WEIRD_EGG ] , " Weird Egg: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_GERUDO_CARD ] , " Gerudo's Card: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_COJIRO ] , " Cojiro: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_POCKET_EGG ] , " Pocket Egg: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MASK_SKULL ] , " Skull Mask: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MASK_SPOOKY ] , " Spooky Mask: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MASK_KEATON ] , " Keaton Mask: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MASK_BUNNY ] , " Bunny Hood: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_ODD_MUSHROOM ] , " Odd Mushroom: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_ODD_POTION ] , " Odd Potion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SAW ] , " Poacher's Saw: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SWORD_BROKEN ] , " Broken Goron Sword: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_PRESCRIPTION ] , " Prescription: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_FROG ] , " Eyeball Frog: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_EYEDROPS ] , " Eye Drops: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_CLAIM_CHECK ] , " Claim Check: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_MINUET ] , " Minuet of Forest: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_BOLERO ] , " Bolero of Fire: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_SERENADE ] , " Serenade of Water: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_REQUIEM ] , " Requiem of Spirit: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_NOCTURNE ] , " Nocturne of Shadow: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_PRELUDE ] , " Prelude of Light: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_LULLABY ] , " Zelda's Lullaby: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_EPONA ] , " Epona's Song: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_SARIA ] , " Saria's Song: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_SUN ] , " Sun's Song: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_TIME ] , " Song of Time: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SONG_STORMS ] , " Song of Storms: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MEDALLION_FOREST ] , " Forest Medallion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MEDALLION_FIRE ] , " Fire Medallion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MEDALLION_WATER ] , " Water Medallion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MEDALLION_SPIRIT ] , " Spirit Medallion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MEDALLION_SHADOW ] , " Shadow Medallion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_MEDALLION_LIGHT ] , " Light Medallion: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_KOKIRI_EMERALD ] , " Kokiri's Emerald: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_GORON_RUBY ] , " Goron's Ruby: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_ZORA_SAPPHIRE ] , " Zora's Sapphire: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_KEY_BOSS ] , " Ganon's Boss Key: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_SINGLE_MAGIC ] , " Magic: " ) ;
strcpy ( itemTimestampDisplayName [ ITEM_DOUBLE_DEFENSE ] , " Double Defense: " ) ;
2022-11-22 20:04:40 -05:00
// Other events
2023-04-03 00:06:55 -04:00
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_GOHMA ] , " Gohma Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_KING_DODONGO ] , " KD Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_BARINADE ] , " Barinade Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_PHANTOM_GANON ] , " PG Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_VOLVAGIA ] , " Volvagia Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_MORPHA ] , " Morpha Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_BONGO_BONGO ] , " Bongo Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_TWINROVA ] , " Twinrova Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_GANONDORF ] , " Ganondorf Defeated: " ) ;
strcpy ( itemTimestampDisplayName [ TIMESTAMP_DEFEAT_GANON ] , " Ganon Defeated: " ) ;
2023-06-01 21:40:10 -04:00
strcpy ( itemTimestampDisplayName [ TIMESTAMP_BOSSRUSH_FINISH ] , " Boss Rush Finished: " ) ;
2023-05-22 10:56:44 -04:00
strcpy ( itemTimestampDisplayName [ TIMESTAMP_FOUND_GREG ] , " Greg Found: " ) ;
2023-09-26 09:45:37 -04:00
strcpy ( itemTimestampDisplayName [ TIMESTAMP_TRIFORCE_COMPLETED ] , " Triforce Completed: " ) ;
2022-11-22 20:04:40 -05:00
}
void SetupDisplayColors ( ) {
for ( int i = 0 ; i < TIMESTAMP_MAX ; i + + ) {
switch ( i ) {
case ITEM_SONG_MINUET :
case ITEM_KOKIRI_EMERALD :
case ITEM_SONG_SARIA :
case ITEM_MEDALLION_FOREST :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_DEFEAT_GOHMA :
case TIMESTAMP_DEFEAT_PHANTOM_GANON :
2023-02-26 23:04:47 -05:00
case TIMESTAMP_FOUND_GREG :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_GREEN ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_BOLERO :
case ITEM_GORON_RUBY :
case ITEM_MEDALLION_FIRE :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_DEFEAT_KING_DODONGO :
case TIMESTAMP_DEFEAT_VOLVAGIA :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_RED ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_SERENADE :
case ITEM_ZORA_SAPPHIRE :
case ITEM_MEDALLION_WATER :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_DEFEAT_BARINADE :
case TIMESTAMP_DEFEAT_MORPHA :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_BLUE ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_LULLABY :
case ITEM_SONG_NOCTURNE :
case ITEM_MEDALLION_SHADOW :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_DEFEAT_BONGO_BONGO :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_PURPLE ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_EPONA :
case ITEM_SONG_REQUIEM :
case ITEM_MEDALLION_SPIRIT :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_DEFEAT_TWINROVA :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_ORANGE ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_SUN :
case ITEM_SONG_PRELUDE :
case ITEM_MEDALLION_LIGHT :
case ITEM_ARROW_LIGHT :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_DEFEAT_GANONDORF :
case TIMESTAMP_DEFEAT_GANON :
2023-09-26 09:45:37 -04:00
case TIMESTAMP_TRIFORCE_COMPLETED :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_YELLOW ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_STORMS :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_GREY ;
2022-11-22 20:04:40 -05:00
break ;
case ITEM_SONG_TIME :
2023-06-01 21:40:10 -04:00
case TIMESTAMP_BOSSRUSH_FINISH :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_LIGHT_BLUE ;
2022-11-22 20:04:40 -05:00
break ;
default :
2023-04-03 00:06:55 -04:00
itemTimestampDisplayColor [ i ] = COLOR_WHITE ;
2022-11-22 20:04:40 -05:00
break ;
}
}
}
2023-06-03 15:27:45 -04:00
void GameplayStatsWindow : : InitElement ( ) {
2022-11-22 20:04:40 -05:00
SetupDisplayNames ( ) ;
SetupDisplayColors ( ) ;
2023-05-27 18:09:24 -04:00
2023-05-11 15:02:04 -04:00
SaveManager : : Instance - > AddLoadFunction ( " sohStats " , 1 , LoadStatsVersion1 ) ;
2023-05-15 20:21:14 -04:00
// Add main section save, no parent
SaveManager : : Instance - > AddSaveFunction ( " sohStats " , 1 , SaveStats , true , SECTION_PARENT_NONE ) ;
// Add subsections, parent of "sohStats". Not sure how to do this without the redundant references to "SaveStats"
SaveManager : : Instance - > AddSaveFunction ( " entrances " , 1 , SaveStats , false , SECTION_ID_STATS ) ;
SaveManager : : Instance - > AddSaveFunction ( " scenes " , 1 , SaveStats , false , SECTION_ID_STATS ) ;
2023-05-12 18:18:26 -04:00
SaveManager : : Instance - > AddInitFunction ( InitStats ) ;
2023-05-27 18:09:24 -04:00
}