diff --git a/soh/assets/custom/textures/parameter_static/gSplitEntrance.rgba32.png b/soh/assets/custom/textures/parameter_static/gSplitEntrance.rgba32.png new file mode 100644 index 00000000000..f53685c22e5 Binary files /dev/null and b/soh/assets/custom/textures/parameter_static/gSplitEntrance.rgba32.png differ diff --git a/soh/assets/custom/textures/parameter_static/gWTriforcePiece.rgba32.png b/soh/assets/custom/textures/parameter_static/gWTriforcePiece.rgba32.png new file mode 100644 index 00000000000..dfdd947f551 Binary files /dev/null and b/soh/assets/custom/textures/parameter_static/gWTriforcePiece.rgba32.png differ diff --git a/soh/assets/soh_assets.h b/soh/assets/soh_assets.h index 3f9039e35f0..c0acacf218f 100644 --- a/soh/assets/soh_assets.h +++ b/soh/assets/soh_assets.h @@ -97,6 +97,12 @@ static const ALIGN_ASSET(2) char gArrowDownTex[] = dgArrowDown; #define dgTriforcePiece "__OTR__textures/parameter_static/gTriforcePiece" static const ALIGN_ASSET(2) char gTriforcePieceTex[] = dgTriforcePiece; +#define dgWTriforcePiece "__OTR__textures/parameter_static/gWTriforcePiece" +static const ALIGN_ASSET(2) char gWTriforcePieceTex[] = dgWTriforcePiece; + +#define dgSplitEntrance "__OTR__textures/parameter_static/gSplitEntrance" +static const ALIGN_ASSET(2) char gSplitEntranceTex[] = dgSplitEntrance; + #define dgBossSoul "__OTR__textures/parameter_static/gBossSoul" static const ALIGN_ASSET(2) char gBossSoulTex[] = dgBossSoul; diff --git a/soh/build.c b/soh/build.c new file mode 100644 index 00000000000..8ca4a9692e4 --- /dev/null +++ b/soh/build.c @@ -0,0 +1,9 @@ +#include + +const char gBuildVersion[] = "Sulu Bravo (8.0.5)"; +const u16 gBuildVersionMajor = 8; +const u16 gBuildVersionMinor = 0; +const u16 gBuildVersionPatch = 5; +const char gBuildTeam[] = "github.com/harbourmasters"; +const char gBuildDate[] = __DATE__ " " __TIME__; +const char gBuildMakeOption[] = ""; diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 46f306a3c83..62c74d8a1bf 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -25,7 +25,9 @@ DEFINE_HOOK(OnActorUpdate, (void* actor)); DEFINE_HOOK(OnActorKill, (void* actor)); DEFINE_HOOK(OnEnemyDefeat, (void* actor)); DEFINE_HOOK(OnBossDefeat, (void* actor)); +DEFINE_HOOK(OnTimestamp, (u8 item)); DEFINE_HOOK(OnPlayerBonk, ()); +DEFINE_HOOK(OnPlayerHealthChange, (int16_t amount)); DEFINE_HOOK(OnPlayDestroy, ()); DEFINE_HOOK(OnPlayDrawEnd, ()); DEFINE_HOOK(OnVanillaBehavior, (GIVanillaBehavior flag, bool* result, va_list originalArgs)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 24efb40c151..7a6362f1e82 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -107,10 +107,18 @@ void GameInteractor_ExecuteOnBossDefeat(void* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +void GameInteractor_ExecuteOnTimestamp (u8 item) { + GameInteractor::Instance->ExecuteHooks(item); +} + void GameInteractor_ExecuteOnPlayerBonk() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount) { + GameInteractor::Instance->ExecuteHooks(amount); +} + void GameInteractor_ExecuteOnPlayDestroy() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index dccc9930ae2..30b5d10d6f3 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -24,7 +24,9 @@ void GameInteractor_ExecuteOnActorUpdate(void* actor); void GameInteractor_ExecuteOnActorKill(void* actor); void GameInteractor_ExecuteOnEnemyDefeat(void* actor); void GameInteractor_ExecuteOnBossDefeat(void* actor); +void GameInteractor_ExecuteOnTimestamp (u8 item); void GameInteractor_ExecuteOnPlayerBonk(); +void GameInteractor_ExecuteOnPlayerHealthChange(int16_t amount); void GameInteractor_ExecuteOnOcarinaSongAction(); void GameInteractor_ExecuteOnShopSlotChangeHooks(uint8_t cursorIndex, int16_t price); void GameInteractor_ExecuteOnPlayDestroy(); diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp index 82a4b3b6e17..aa855fe2fb3 100644 --- a/soh/soh/Enhancements/gameplaystats.cpp +++ b/soh/soh/Enhancements/gameplaystats.cpp @@ -16,6 +16,8 @@ extern "C" { #include "soh/Enhancements/enhancementTypes.h" #include "soh/OTRGlobals.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" + extern "C" { #include #include "variables.h" @@ -241,7 +243,7 @@ const char* const countMappings[] = { #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) -char itemTimestampDisplayName[TIMESTAMP_MAX][21] = { "" }; +char itemTimestampDisplayName[TIMESTAMP_MAX][23] = { "" }; ImVec4 itemTimestampDisplayColor[TIMESTAMP_MAX]; typedef struct { @@ -770,19 +772,20 @@ void SetupDisplayNames() { strcpy(itemTimestampDisplayName[ITEM_DOUBLE_DEFENSE], "Double Defense: "); // Other events - 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: "); - strcpy(itemTimestampDisplayName[TIMESTAMP_BOSSRUSH_FINISH], "Boss Rush Finished: "); - strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_GREG], "Greg Found: "); - strcpy(itemTimestampDisplayName[TIMESTAMP_TRIFORCE_COMPLETED], "Triforce Completed: "); + 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: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_BOSSRUSH_FINISH], "Boss Rush Finished: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_FOUND_GREG], "Greg Found: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_TRIFORCE_COMPLETED], "Triforce Completed: "); + strcpy(itemTimestampDisplayName[TIMESTAMP_TIMESPLIT_COMPLETED], "Time Split Completed: "); } void SetupDisplayColors() { @@ -830,6 +833,7 @@ void SetupDisplayColors() { case TIMESTAMP_DEFEAT_GANONDORF: case TIMESTAMP_DEFEAT_GANON: case TIMESTAMP_TRIFORCE_COMPLETED: + case TIMESTAMP_TIMESPLIT_COMPLETED: itemTimestampDisplayColor[i] = COLOR_YELLOW; break; case ITEM_SONG_STORMS: diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h index 528da33f93f..75fdc19ce4c 100644 --- a/soh/soh/Enhancements/gameplaystats.h +++ b/soh/soh/Enhancements/gameplaystats.h @@ -36,7 +36,8 @@ typedef enum { /* 0xA9 */ TIMESTAMP_BOSSRUSH_FINISH, // z_boss_ganon2.c /* 0xAA */ TIMESTAMP_FOUND_GREG, // z_parameter.c /* 0xAA */ TIMESTAMP_TRIFORCE_COMPLETED, // z_parameter.c - /* 0xAB */ TIMESTAMP_MAX + /* 0xAB */ TIMESTAMP_TIMESPLIT_COMPLETED, // TimeSplits.cpp + /* 0xAC */ TIMESTAMP_MAX }GameplayStatTimestamp; diff --git a/soh/soh/Enhancements/timesplits/TimeSplits.cpp b/soh/soh/Enhancements/timesplits/TimeSplits.cpp new file mode 100644 index 00000000000..7c0fe935485 --- /dev/null +++ b/soh/soh/Enhancements/timesplits/TimeSplits.cpp @@ -0,0 +1,739 @@ +#include "TimeSplits.h" +#include "soh/UIWidgets.hpp" +#include "soh/Enhancements/gameplaystats.h" +#include "soh/SaveManager.h" +#include "soh/util.h" +#include +#include "include/z64item.h" + +#include +#include + +#include "soh/OTRGlobals.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/debugger/debugSaveEditor.h" +#include "soh_assets.h" +#include "assets/textures/parameter_static/parameter_static.h" + +extern "C" { + extern SaveContext gSaveContext; + extern PlayState* gPlayState; +} + +// ImVec4 Colors +#define COLOR_WHITE ImVec4(1.00f, 1.00f, 1.00f, 1.00f) +#define COLOR_LIGHT_RED ImVec4(1.0f, 0.05f, 0.0f, 1.0f) +#define COLOR_RED ImVec4(1.00f, 0.00f, 0.00f, 1.00f) +#define COLOR_LIGHT_GREEN ImVec4(0.52f, 1.0f, 0.23f, 1.0f) +#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) + +using json = nlohmann::json; + +// NEW START +static uint32_t splitBestTimeDisplay; +static uint32_t popupID = 0; + +static ImVec4 windowColor = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); +static ImVec4 splitStatusColor = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); +static ImVec4 splitTimeColor = ImVec4(0.0f, 0.0f, 0.0f, 1.0f); + +std::vector keys; + +char listNameBuf[25]; +const char* lastLoadedlistName; +bool displayPopup = false; + +std::vector splitList; +std::vector emptyList; + +std::vector splitObjectList = { + { SPLIT_ITEM, ITEM_STICK, "Deku Stick", "ITEM_STICK", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_NUT, "Deku Nut", "ITEM_NUT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOW, "Fairy Bow", "ITEM_BOW", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_ARROW_FIRE, "Fire Arrow", "ITEM_ARROW_FIRE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_DINS_FIRE, "Din's Fire", "ITEM_DINS_FIRE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SLINGSHOT, "Fairy Slingshot", "ITEM_SLINGSHOT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_OCARINA_FAIRY, "Fairy Ocarina", "ITEM_OCARINA_FAIRY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_OCARINA_TIME, "Ocarina of Time", "ITEM_OCARINA_TIME", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOMBCHU, "Bombchu", "ITEM_BOMBCHU", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_HOOKSHOT, "Hookshot", "ITEM_HOOKSHOT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_LONGSHOT, "Longshot", "ITEM_LONGSHOT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_ARROW_ICE, "Ice Arrow", "ITEM_ARROW_ICE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_FARORES_WIND, "Farore's Wind", "ITEM_FARORES_WIND", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOOMERANG, "Boomerang", "ITEM_BOOMERANG", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_LENS, "Lens of Truth", "ITEM_LENS", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BEAN, "Magic Bean", "ITEM_BEAN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_HAMMER, "Megaton Hammer", "ITEM_HAMMER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_ARROW_LIGHT, "Light Arrow", "ITEM_ARROW_LIGHT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_NAYRUS_LOVE, "Nayru's Love", "ITEM_NAYRUS_LOVE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOTTLE, "Empty Bottle", "ITEM_BOTTLE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_POTION_RED, "Red Potion", "ITEM_POTION_RED", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_POTION_GREEN, "Green Potion", "ITEM_POTION_GREEN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_POTION_BLUE, "Blue Potion", "ITEM_POTION_BLUE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_FAIRY, "Bottled Fairy", "ITEM_FAIRY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_FISH, "Fish", "ITEM_FISH", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MILK_BOTTLE, "Milk", "ITEM_MILK_BOTTLE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_LETTER_RUTO, "Ruto's Letter", "ITEM_LETTER_RUTO", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BLUE_FIRE, "Blue Fire", "ITEM_BLUE_FIRE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BUG, "Bug", "ITEM_BUG", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BIG_POE, "Big Poe", "ITEM_BIG_POE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_POE, "Poe", "ITEM_POE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_WEIRD_EGG, "Weird Egg", "ITEM_WEIRD_EGG", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_CHICKEN, "Chicken", "ITEM_CHICKEN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_LETTER_ZELDA, "Zelda's Letter", "ITEM_LETTER_ZELDA", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_KEATON, "Keaton Mask", "ITEM_MASK_KEATON", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_SKULL, "Skull Mask", "ITEM_MASK_SKULL", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_SPOOKY, "Spooky Mask", "ITEM_MASK_SPOOKY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_BUNNY, "Bunny Hood", "ITEM_MASK_BUNNY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_GORON, "Goron Mask", "ITEM_MASK_GORON", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_ZORA, "Zora Mask", "ITEM_MASK_ZORA", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_GERUDO, "Gerudo Mask", "ITEM_MASK_GERUDO", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MASK_TRUTH, "Mask of Truth", "ITEM_MASK_TRUTH", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_POCKET_EGG, "Pocket Egg", "ITEM_POCKET_EGG", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_POCKET_CUCCO, "Pocket Cucco", "ITEM_POCKET_CUCCO", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_COJIRO, "Cojiro", "ITEM_COJIRO", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_ODD_MUSHROOM, "Odd Mushroom", "ITEM_ODD_MUSHROOM", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_ODD_POTION, "Odd Potion", "ITEM_ODD_POTION", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SAW, "Poacher's Saw", "ITEM_SAW", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SWORD_BROKEN, "Goron's Sword (Broken)", "ITEM_SWORD_BROKEN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_PRESCRIPTION, "Prescription", "ITEM_PRESCRIPTION", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_FROG, "Eyeball Frog", "ITEM_FROG", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_EYEDROPS, "Eye Drops", "ITEM_EYEDROPS", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_CLAIM_CHECK, "Claim Check", "ITEM_CLAIM_CHECK", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SWORD_KOKIRI, "Kokiri Sword", "ITEM_SWORD_KOKIRI", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SWORD_MASTER, "Master Sword", "ITEM_SWORD_MASTER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SWORD_BGS, "Giant's Knife & Biggoron's Sword", "ITEM_SWORD_BGS", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SHIELD_DEKU, "Deku Shield", "ITEM_SHIELD_DEKU", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SHIELD_HYLIAN, "Hylian Shield", "ITEM_SHIELD_HYLIAN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SHIELD_MIRROR, "Mirror Shield", "ITEM_SHIELD_MIRROR", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_TUNIC_GORON, "Goron Tunic", "ITEM_TUNIC_GORON", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_TUNIC_ZORA, "Zora Tunic", "ITEM_TUNIC_ZORA", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOOTS_IRON, "Iron Boots", "ITEM_BOOTS_IRON", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOOTS_HOVER, "Hover Boots", "ITEM_BOOTS_HOVER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BULLET_BAG_30, "Bullet Bag (30)", "ITEM_BULLET_BAG_30", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BULLET_BAG_40, "Bullet Bag (40)", "ITEM_BULLET_BAG_40", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BULLET_BAG_50, "Bullet Bag (50)", "ITEM_BULLET_BAG_50", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_QUIVER_30, "Quiver (30)", "ITEM_QUIVER_30", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_QUIVER_40, "Big Quiver (40)", "ITEM_QUIVER_40", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_QUIVER_50, "Biggest Quiver (50)", "ITEM_QUIVER_50", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOMB_BAG_20, "Bomb Bag (20)", "ITEM_BOMB_BAG_20", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOMB_BAG_30, "Big Bomb Bag (30)", "ITEM_BOMB_BAG_30", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BOMB_BAG_40, "Biggest Bomb Bag (40)", "ITEM_BOMB_BAG_40", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_BRACELET, "Goron's Bracelet", "ITEM_BRACELET", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_GAUNTLETS_SILVER, "Silver Gauntlets", "ITEM_GAUNTLETS_SILVER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_GAUNTLETS_GOLD, "Golden Gauntlets", "ITEM_GAUNTLETS_GOLD", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SCALE_SILVER, "Silver Scale", "ITEM_SCALE_SILVER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SCALE_GOLDEN, "Golden Scale", "ITEM_SCALE_GOLDEN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SWORD_KNIFE, "Giant's Knife (Broken)", "ITEM_SWORD_KNIFE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_WALLET_ADULT, "Adult's Wallet", "ITEM_WALLET_ADULT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_WALLET_GIANT, "Giant's Wallet", "ITEM_WALLET_GIANT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_FISHING_POLE, "Fishing Pole", "ITEM_FISHING_POLE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_MINUET, "Minuet of Forest", "QUEST_SONG_MINUET", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_BOLERO, "Bolero of Fire", "QUEST_SONG_BOLERO", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_SERENADE, "Serenade of Water", "QUEST_SONG_SERENADE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_REQUIEM, "Requiem of Spirit", "QUEST_SONG_REQUIEM", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_NOCTURNE, "Nocturne of Shadow", "QUEST_SONG_NOCTURNE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_PRELUDE, "Prelude of Light", "QUEST_SONG_PRELUDE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_LULLABY, "Zelda's Lullaby", "QUEST_SONG_LULLABY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_EPONA, "Epona's Song", "QUEST_SONG_EPONA", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_SARIA, "Saria's Song", "QUEST_SONG_SARIA", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_SUN, "Sun's Song", "QUEST_SONG_SUN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_TIME, "Song of Time", "QUEST_SONG_TIME", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SONG_STORMS, "Song of Storms", "QUEST_SONG_STORMS", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MEDALLION_FOREST, "Forest Medallion", "QUEST_MEDALLION_FOREST", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MEDALLION_FIRE, "Fire Medallion", "QUEST_MEDALLION_FIRE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MEDALLION_WATER, "Water Medallion", "QUEST_MEDALLION_WATER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MEDALLION_SPIRIT, "Spirit Medallion", "QUEST_MEDALLION_SPIRIT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MEDALLION_SHADOW, "Shadow Medallion", "QUEST_MEDALLION_SHADOW", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_MEDALLION_LIGHT, "Light Medallion", "QUEST_MEDALLION_LIGHT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_KOKIRI_EMERALD, "Kokiri's Emerald", "QUEST_KOKIRI_EMERALD", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_GORON_RUBY, "Goron's Ruby", "QUEST_GORON_RUBY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_ZORA_SAPPHIRE, "Zora's Sapphire", "QUEST_ZORA_SAPPHIRE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_STONE_OF_AGONY, "Stone of Agony", "QUEST_STONE_OF_AGONY", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_GERUDO_CARD, "Gerudo's Card", "QUEST_GERUDO_CARD", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SKULL_TOKEN, "Skulltula Token", "QUEST_SKULL_TOKEN", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_SINGLE_MAGIC, "Magic Meter", "ITEM_MAGIC_SMALL", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_DOUBLE_MAGIC, "Double Magic", "ITEM_MAGIC_LARGE", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_DOUBLE_DEFENSE, "Double Defense", "ITEM_HEART_CONTAINER", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_STICK_UPGRADE_20, "Deku Stick Upgrade (20)", "ITEM_STICK", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_STICK_UPGRADE_30, "Deku Stick Upgrade (30)", "ITEM_STICK", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_NUT_UPGRADE_30, "Deku Nut Upgrade (30)", "ITEM_NUT", COLOR_WHITE }, + { SPLIT_ITEM, ITEM_NUT_UPGRADE_40, "Deku Nut Upgrade (40)", "ITEM_NUT", COLOR_WHITE }, + { SPLIT_BOSS, ACTOR_BOSS_GOMA, "Queen Gohma", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_LIGHT_GREEN }, + { SPLIT_BOSS, ACTOR_BOSS_DODONGO, "King Dodongo", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_LIGHT_RED }, + { SPLIT_BOSS, ACTOR_BOSS_VA, "Barinade", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_LIGHT_BLUE }, + { SPLIT_BOSS, ACTOR_BOSS_GANONDROF, "Phantom Ganon", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_GREEN }, + { SPLIT_BOSS, ACTOR_BOSS_FD2, "Volvagia", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_RED }, + { SPLIT_BOSS, ACTOR_BOSS_MO, "Morpha", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_BLUE }, + { SPLIT_BOSS, ACTOR_BOSS_SST, "Bongo Bongo", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_PURPLE }, + { SPLIT_BOSS, ACTOR_BOSS_TW, "Twinrova", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_ORANGE }, + { SPLIT_BOSS, ACTOR_BOSS_GANON, "Ganondorf", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_GREY }, + { SPLIT_BOSS, ACTOR_BOSS_GANON2, "Ganon", "SPECIAL_TRIFORCE_PIECE_WHITE", COLOR_YELLOW }, + { SPLIT_ENTRANCE, SCENE_DEKU_TREE, "Enter Deku Tree", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_DODONGOS_CAVERN, "Enter Dodongos Cavern", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_JABU_JABU, "Enter Jabu Jabu's Belly", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_FOREST_TEMPLE, "Enter Forest Temple", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_FIRE_TEMPLE, "Enter Fire Temple", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_WATER_TEMPLE, "Enter Water Temple", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_SPIRIT_TEMPLE, "Enter Spirit Temple", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_SHADOW_TEMPLE, "Enter Shadow Temple", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_BOTTOM_OF_THE_WELL, "Enter Bottom of the Well", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_ICE_CAVERN, "Enter Ice Cavern", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_GANONS_TOWER, "Enter Ganons Tower", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_GERUDO_TRAINING_GROUND, "Enter Gerudo Training Grounds", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_THIEVES_HIDEOUT, "Enter Thieves Hideout", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_INSIDE_GANONS_CASTLE, "Enter Ganons Castle", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR, "Enter Tower Collapse Interior", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_ENTRANCE, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, "Enter Ganons Castle Collapse", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_MISC, SCENE_ZORAS_RIVER, "Lost Woods Escape", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_MISC, SCENE_LOST_WOODS, "Forest Escape", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, + { SPLIT_MISC, SCENE_KAKARIKO_VILLAGE, "Watchtower Death", "SPECIAL_SPLIT_ENTRANCE", COLOR_WHITE }, +}; + +std::map> popupList = { + // { ITEM_STICK, { ITEM_STICK, ITEM_STICK_UPGRADE_20, ITEM_STICK_UPGRADE_30, ITEM_STICK } }, + // { ITEM_NUT, { ITEM_NUT, ITEM_NUT_UPGRADE_30, ITEM_NUT_UPGRADE_40 } }, + // { ITEM_BOMB, { ITEM_BOMB_BAG_20, ITEM_BOMB_BAG_30, ITEM_BOMB_BAG_40 } }, + { ITEM_BOW, { ITEM_QUIVER_30, ITEM_QUIVER_40, ITEM_QUIVER_50 } }, + { ITEM_SLINGSHOT, { ITEM_BULLET_BAG_30, ITEM_BULLET_BAG_40, ITEM_BULLET_BAG_50 } }, + { ITEM_OCARINA_FAIRY, { ITEM_OCARINA_FAIRY, ITEM_OCARINA_TIME } }, + { ITEM_HOOKSHOT, { ITEM_HOOKSHOT, ITEM_LONGSHOT } }, + // { ITEM_BOTTLE, { } }, + { ITEM_WEIRD_EGG, { ITEM_WEIRD_EGG, ITEM_CHICKEN, ITEM_LETTER_ZELDA, ITEM_MASK_KEATON, + ITEM_MASK_SKULL, ITEM_MASK_SPOOKY, ITEM_MASK_BUNNY, ITEM_MASK_GORON, + ITEM_MASK_ZORA, ITEM_MASK_GERUDO, ITEM_MASK_TRUTH } }, + { ITEM_POCKET_EGG, { ITEM_POCKET_EGG, ITEM_POCKET_CUCCO, ITEM_COJIRO, ITEM_ODD_MUSHROOM, + ITEM_ODD_POTION, ITEM_SAW, ITEM_SWORD_BROKEN, ITEM_PRESCRIPTION, + ITEM_FROG, ITEM_EYEDROPS, ITEM_CLAIM_CHECK } }, + { ITEM_BRACELET, { ITEM_BRACELET, ITEM_GAUNTLETS_SILVER, ITEM_GAUNTLETS_GOLD } }, + { ITEM_SCALE_SILVER, { ITEM_SCALE_SILVER, ITEM_SCALE_GOLDEN } }, +}; + +// NEW END + +std::string formatTimestampTimeSplit(uint32_t value) { + uint32_t sec = value / 10; + uint32_t hh = sec / 3600; + uint32_t mm = (sec - hh * 3600) / 60; + uint32_t ss = sec - hh * 3600 - mm * 60; + uint32_t ds = value % 10; + return fmt::format("{}:{:0>2}:{:0>2}.{}", hh, mm, ss, ds); +} + +void TimeSplitCompleteSplits() { + gSaveContext.sohStats.itemTimestamp[TIMESTAMP_DEFEAT_GANON] = GAMEPLAYSTAT_TOTAL_TIME; + gSaveContext.sohStats.gameComplete = true; +} + +//void TimeSplitSkipSplit(uint32_t splitID) { +// splitStatus[splitID] = 3; +// if (splitID + 1 == splitItem.size()) { +// TimeSplitCompleteSplits(); +// } else { +// splitStatus[splitID + 1] = 2; +// } +// +//} + +// NEW START +nlohmann::json ImVec4_to_json(const ImVec4& vec) { + return nlohmann::json{ + {"x", vec.x}, + {"y", vec.y}, + {"z", vec.z}, + {"w", vec.w} + }; +} + +ImVec4 json_to_ImVec4(const nlohmann::json& jsonVec) { + return ImVec4(jsonVec["x"], jsonVec["y"], jsonVec["z"], jsonVec["w"]); +} + +nlohmann::json SplitObject_to_json(const SplitObject& split) { + return nlohmann::json{ + {"splitType", split.splitType}, + {"splitID", split.splitID}, + {"splitName", split.splitName}, + {"splitImage", split.splitImage}, + {"splitTint", ImVec4_to_json(split.splitTint)}, + {"splitTimeCurrent", split.splitTimeCurrent}, + {"splitTimeBest", split.splitTimeBest}, + {"splitTimePreviousBest", split.splitTimePreviousBest}, + {"splitTimeStatus", split.splitTimeStatus}, + {"splitSkullTokencount", split.splitSkullTokenCount} + }; +} + +SplitObject json_to_SplitObject(const nlohmann::json& jsonSplit) { + SplitObject split; + split.splitType = jsonSplit["splitType"]; + split.splitID = jsonSplit["splitID"]; + split.splitName = jsonSplit["splitName"].get(); + split.splitImage = jsonSplit["splitImage"].get(); + split.splitTint = json_to_ImVec4(jsonSplit["splitTint"]); + split.splitTimeCurrent = jsonSplit["splitTimeCurrent"]; + split.splitTimeBest = jsonSplit["splitTimeBest"]; + split.splitTimePreviousBest = jsonSplit["splitTimePreviousBest"]; + split.splitTimeStatus = jsonSplit["splitTimeStatus"]; + split.splitSkullTokenCount = jsonSplit["splitSkullTokenCount"]; + return split; +} + +void TimeSplitsFileManagement(uint32_t action, const char* listEntry, std::vector listData) { + std::string filename = "timesplitdata.json"; + json saveFile; + json listArray = nlohmann::json::array(); + + std::ifstream inputFile(filename); + if (inputFile.is_open()) { + inputFile >> saveFile; + inputFile.close(); + } + + if (action == ACTION_SAVE) { + for (auto& data : listData) { + listArray.push_back(SplitObject_to_json(data)); + } + saveFile[listEntry] = listArray; + + // Update Save File on Disk + std::ofstream outputFile(filename); + if (outputFile.is_open()) { + outputFile << saveFile.dump(4); + outputFile.close(); + } + } + + if (action == ACTION_LOAD) { + if (saveFile.contains(listEntry)) { + listArray = saveFile[listEntry]; + splitList.clear(); + + for (auto& data : listArray) { + splitList.push_back(json_to_SplitObject(data)); + } + } + } + + if (action == ACTION_UPDATE) { + for (auto& update : listData) { + if (update.splitTimeBest < update.splitTimePreviousBest) { + update.splitTimePreviousBest = update.splitTimeBest; + } + } + } + + if (action == ACTION_COLLECT) { + keys.clear(); + for (auto& data : saveFile.items()) { + keys.push_back(data.key()); + } + if (keys.size() == 0) { + keys.push_back("No Saved Lists"); + } + } +} + +void TimeSplitsPopUpContext() { + int rowIndex = 0; + if (ImGui::BeginPopup("TimeSplitsPopUp") && popupID) { + for (auto item : popupList[popupID]) { + auto findID = std::find_if(splitObjectList.begin(), splitObjectList.end(), [&](const SplitObject& obj) { return obj.splitID == item; }); + if (findID == splitObjectList.end()) { + continue; + } + + SplitObject& popupObject = *findID; + if (ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(popupObject.splitImage), + ImVec2(26.0f, 26.0f), ImVec2(0, 0), ImVec2(1, 1), 2.0f, ImVec4(0, 0, 0, 0), popupObject.splitTint)) { + splitList.push_back(popupObject); + if (splitList.size() == 1) { + splitList[0].splitTimeStatus = SPLIT_ACTIVE; + } else { + splitList[splitList.size() - 1].splitTimeStatus = SPLIT_INACTIVE; + } + ImGui::CloseCurrentPopup(); + displayPopup = false; + } + if (rowIndex != 5) { + ImGui::SameLine(); + } + rowIndex++; + } + ImGui::EndPopup(); + } +} + +void TimeSplitsItemSplitEvent(u8 item) { + uint32_t index = 0; + if (item == ITEM_NUTS_5 || item == ITEM_NUTS_10) { + item = ITEM_NUT; + } else if (item == ITEM_STICKS_5 || item == ITEM_STICKS_10) { + item = ITEM_STICK; + } + if (item == ITEM_SKULL_TOKEN) { + auto it = std::find_if(splitList.begin(), splitList.end(), [item](const SplitObject& split) { + if (split.splitSkullTokenCount == gSaveContext.inventory.gsTokens + 1) { + return split.splitID == item; + } + }); + if (it == splitList.end()) { + return; + } + } + + for (auto& split : splitList) { + if (split.splitType == SPLIT_ITEM) { + if (item == split.splitID) { + if (split.splitTimeStatus == SPLIT_ACTIVE) { + split.splitTimeCurrent = GAMEPLAYSTAT_TOTAL_TIME; + split.splitTimeStatus = SPLIT_COLLECTED; + if (split.splitTimeBest > GAMEPLAYSTAT_TOTAL_TIME || split.splitTimeBest == 0) { + split.splitTimeBest = GAMEPLAYSTAT_TOTAL_TIME; + } + if (split.splitTimePreviousBest == 0) { + split.splitTimePreviousBest = GAMEPLAYSTAT_TOTAL_TIME; + } + if (index == splitList.size() - 1) { + // Completed List + TimeSplitCompleteSplits(); + } else { + splitList[index + 1].splitTimeStatus = SPLIT_ACTIVE; + } + } + } + } + index++; + } +} + +void TimeSplitsBossSplitEvent(Actor* actor) { + uint32_t index = 0; + for (auto& split : splitList) { + if (split.splitType == SPLIT_BOSS) { + if (actor->id == split.splitID) { + if (split.splitTimeStatus == SPLIT_ACTIVE) { + split.splitTimeCurrent = GAMEPLAYSTAT_TOTAL_TIME; + split.splitTimeStatus = SPLIT_COLLECTED; + if (split.splitTimeBest > GAMEPLAYSTAT_TOTAL_TIME || split.splitTimeBest == 0) { + split.splitTimeBest = GAMEPLAYSTAT_TOTAL_TIME; + } + if (split.splitTimePreviousBest == 0) { + split.splitTimePreviousBest = GAMEPLAYSTAT_TOTAL_TIME; + } + if (index == splitList.size() - 1) { + // Completed List + TimeSplitCompleteSplits(); + } else { + splitList[index + 1].splitTimeStatus = SPLIT_ACTIVE; + } + } + } + } + index++; + } +} + +void TimeSplitsSceneSplitEvent(int16_t scene) { + uint32_t setType = SPLIT_ENTRANCE; + + if ((scene == SCENE_ZORAS_RIVER && gSaveContext.entranceIndex == ENTR_ZORAS_RIVER_4) || + (scene == SCENE_LOST_WOODS && (gSaveContext.entranceIndex == ENTR_LOST_WOODS_9 || + gSaveContext.entranceIndex == ENTR_LOST_WOODS_0)) || scene == SCENE_KAKARIKO_VILLAGE) { + setType = SPLIT_MISC; + } + + uint32_t index = 0; + for (auto& split : splitList) { + if (split.splitType == setType) { + if (scene == split.splitID) { + if (split.splitTimeStatus == SPLIT_ACTIVE) { + split.splitTimeCurrent = GAMEPLAYSTAT_TOTAL_TIME; + split.splitTimeStatus = SPLIT_COLLECTED; + if (split.splitTimeBest > GAMEPLAYSTAT_TOTAL_TIME || split.splitTimeBest == 0) { + split.splitTimeBest = GAMEPLAYSTAT_TOTAL_TIME; + } + if (split.splitTimePreviousBest == 0) { + split.splitTimePreviousBest = GAMEPLAYSTAT_TOTAL_TIME; + } + if (index == splitList.size() - 1) { + // Completed List + TimeSplitCompleteSplits(); + } else { + splitList[index + 1].splitTimeStatus = SPLIT_ACTIVE; + } + } + } + } + index++; + } +} + +void TimeSplitsSplitBestTimeDisplay(uint32_t status, uint32_t currentBestTime, uint32_t previousBestTime) { + if (status == SPLIT_ACTIVE) { + if (GAMEPLAYSTAT_TOTAL_TIME > previousBestTime) { + splitTimeColor = COLOR_RED; + splitBestTimeDisplay = (GAMEPLAYSTAT_TOTAL_TIME - previousBestTime); + } + if (GAMEPLAYSTAT_TOTAL_TIME == previousBestTime) { + splitTimeColor = COLOR_WHITE; + splitBestTimeDisplay = GAMEPLAYSTAT_TOTAL_TIME; + } + if (GAMEPLAYSTAT_TOTAL_TIME < previousBestTime) { + splitTimeColor = COLOR_GREEN; + splitBestTimeDisplay = (previousBestTime - GAMEPLAYSTAT_TOTAL_TIME); + } + } + if (status == SPLIT_INACTIVE) { + splitTimeColor = COLOR_WHITE; + splitBestTimeDisplay = currentBestTime; + } + if (status == SPLIT_COLLECTED) { + if (currentBestTime > previousBestTime) { + splitTimeColor = COLOR_RED; + } + if (currentBestTime == previousBestTime) { + splitTimeColor = COLOR_WHITE; + } + if (currentBestTime < previousBestTime) { + splitTimeColor = COLOR_GREEN; + } + splitBestTimeDisplay = currentBestTime; + } +} + +void TimeSplitsDrawSplitsList() { + ImGui::BeginChild("SplitTable", ImVec2(0.0f, ImGui::GetWindowHeight() - 128.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4, 0)); + ImGui::BeginTable("Splits", 5, ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable); + ImGui::TableSetupColumn("Item Image", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHeaderLabel, 27.0f); + ImGui::TableSetupColumn("Item Name"); + ImGui::TableSetupColumn("Current Time", ImGuiTableColumnFlags_WidthFixed, 90.0f); + ImGui::TableSetupColumn("+/-", ImGuiTableColumnFlags_WidthFixed, 80.0f); + ImGui::TableSetupColumn("Prev. Best", ImGuiTableColumnFlags_WidthFixed, 90.0f); + ImGui::TableHeadersRow(); + + for (auto& split : splitList) { + ImGui::TableNextColumn(); + ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(split.splitImage), + ImVec2(26.0f, 26.0f), ImVec2(0, 0), ImVec2(1, 1), 2.0f, ImVec4(0, 0, 0, 0), split.splitTint); + ImGui::TableNextColumn(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 7.0f)); + ImGui::AlignTextToFramePadding(); + ImGui::Text(split.splitName.c_str()); + ImGui::TableNextColumn(); + // Current Time + ImGui::Text((split.splitTimeStatus == SPLIT_ACTIVE) + ? formatTimestampTimeSplit(GAMEPLAYSTAT_TOTAL_TIME).c_str() : (split.splitTimeStatus == SPLIT_COLLECTED) + ? formatTimestampTimeSplit(split.splitTimeCurrent).c_str() : "--:--:-"); + ImGui::TableNextColumn(); + // +/- Difference + TimeSplitsSplitBestTimeDisplay(split.splitTimeStatus, split.splitTimeBest, split.splitTimePreviousBest); + ImGui::TextColored(splitTimeColor, formatTimestampTimeSplit(splitBestTimeDisplay).c_str()); + ImGui::TableNextColumn(); + // Previous Best + ImGui::Text((split.splitTimePreviousBest != 0) ? formatTimestampTimeSplit(split.splitTimePreviousBest).c_str() : "--:--:-"); + ImGui::PopStyleVar(1); + } + ImGui::PopStyleVar(); + ImGui::EndTable(); + ImGui::EndChild(); +} + +void TimeSplitsDrawItemList(uint32_t type) { + ImGui::BeginChild("Item Child"); + ImGui::BeginTable("Item List", 2); + ImGui::TableSetupColumn("Item Image", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoHeaderLabel, 27.0f); + ImGui::TableSetupColumn("Item Name"); + + for (auto& split : splitObjectList) { + if (split.splitType == type) { + ImGui::TableNextColumn(); + ImGui::PushID(split.splitID); + if (ImGui::ImageButton(Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName(split.splitImage), + ImVec2(26.0f, 26.0f), ImVec2(0, 0), ImVec2(1, 1), 2.0f, ImVec4(0, 0, 0, 0), split.splitTint)) { + + if (popupList.contains(split.splitID)) { + popupID = split.splitID; + ImGui::OpenPopup("TimeSplitsPopUp"); + } else { + splitList.push_back(split); + if (splitList.size() == 1) { + splitList[0].splitTimeStatus = SPLIT_ACTIVE; + } else { + splitList[splitList.size() - 1].splitTimeStatus = SPLIT_INACTIVE; + } + } + } + + TimeSplitsPopUpContext(); + ImGui::PopID(); + ImGui::TableNextColumn(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 7.0f)); + ImGui::AlignTextToFramePadding(); + ImGui::Text(split.splitName.c_str()); + ImGui::PopStyleVar(1); + } + } + ImGui::EndTable(); + ImGui::EndChild(); +} + +void TimeSplitsDrawOptionsMenu() { + ImGui::SeparatorText("Background Color"); + if (ImGui::ColorEdit4("Background Color", (float*)&windowColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) { + Color_RGBA8 color; + color.r = windowColor.x; + color.g = windowColor.y; + color.b = windowColor.z; + color.a = windowColor.w; + } + ImGui::SameLine(); + if (ImGui::Button("Reset")) { + windowColor = { 0.0f, 0.0f, 0.0f, 1.0f }; + } + //UIWidgets::PaddedEnhancementCheckbox("Set Last Split as Goal", CVAR_SETTING("TimesplitGoal")); + + ImGui::SeparatorText("Split List Management"); + ImGui::Text("New List Name: "); + ImGui::PushItemWidth(100.0f); + ImGui::InputText("##listName", listNameBuf, 25); + ImGui::PopItemWidth(); + ImGui::SameLine(); + if (ImGui::Button("Create List")) { + TimeSplitsFileManagement(ACTION_SAVE, listNameBuf, splitList); + } + UIWidgets::PaddedSeparator(); + + TimeSplitsFileManagement(ACTION_COLLECT, "", emptyList); + static uint32_t selectedItem = 0; + static std::string listItem = keys[0]; + ImGui::Text("Select List to Load: "); + ImGui::PushItemWidth(150.0f); + if (ImGui::BeginCombo("##listEntries", keys[selectedItem].c_str())) { + for (int i = 0; i < keys.size(); i++) { + bool isSelected = (selectedItem == i); + if (ImGui::Selectable(keys[i].c_str(), isSelected)) { + selectedItem = i; + listItem = keys[i].c_str(); + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + ImGui::SameLine(); + if (ImGui::Button("Load List")) { + TimeSplitsFileManagement(ACTION_LOAD, keys[selectedItem].c_str(), emptyList); + lastLoadedlistName = keys[selectedItem].c_str(); + } + UIWidgets::PaddedSeparator(); + + if (ImGui::Button("Update Splits")) { + TimeSplitsFileManagement(ACTION_UPDATE, lastLoadedlistName, splitList); + } + ImGui::SameLine(); + if (ImGui::Button("Save List")) { + TimeSplitsFileManagement(ACTION_SAVE, lastLoadedlistName, splitList); + } +} + +void TimeSplitsDrawManageList() { + ImGui::BeginTabBar("List Options"); + if (ImGui::BeginTabItem("Inventory")) { + TimeSplitsDrawItemList(SPLIT_ITEM); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Entrances")) { + TimeSplitsDrawItemList(SPLIT_ENTRANCE); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Bosses")) { + TimeSplitsDrawItemList(SPLIT_BOSS); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Miscellaneous")) { + TimeSplitsDrawItemList(SPLIT_MISC); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); +} + +// NEW END + +void InitializeSplitDataFile() { + if (!std::filesystem::exists("timesplitdata.json")) { + json j; + std::ofstream file("timesplitdata.json"); + file << j.dump(4); + file.close(); + } +} + +static bool initialized = false; + +void TimeSplitWindow::DrawElement() { + if (!initialized) { + InitializeSplitDataFile(); + initialized = true; + } + + ImGui::PushStyleColor(ImGuiCol_WindowBg, windowColor); + if (!ImGui::Begin("Time Splitter Window", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) { + ImGui::End(); + ImGui::PopStyleColor(1); + return; + } + ImGui::BeginTabBar("Split Tabs"); + if (ImGui::BeginTabItem("Splits")) { + TimeSplitsDrawSplitsList(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Manage List")) { + TimeSplitsDrawManageList(); + ImGui::EndTabItem(); + } + if (ImGui::BeginTabItem("Options")) { + TimeSplitsDrawOptionsMenu(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); + ImGui::End(); + ImGui::PopStyleColor(); +} + +void TimeSplitWindow::InitElement() { + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("SPECIAL_TRIFORCE_PIECE_WHITE", gWTriforcePieceTex, ImVec4(1, 1, 1, 1)); + Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("SPECIAL_SPLIT_ENTRANCE", gSplitEntranceTex, ImVec4(1, 1, 1, 1)); + + GameInteractor::Instance->RegisterGameHook([](u8 item) { + TimeSplitsItemSplitEvent(item); + }); + + GameInteractor::Instance->RegisterGameHook([](void* refActor) { + TimeSplitsBossSplitEvent((Actor*)refActor); + }); + + GameInteractor::Instance->RegisterGameHook([](int16_t sceneNum) { + if (gPlayState->sceneNum != SCENE_KAKARIKO_VILLAGE) { + TimeSplitsSceneSplitEvent(sceneNum); + } + }); + + GameInteractor::Instance->RegisterGameHook([](int16_t amount) { + if (gPlayState->sceneNum == SCENE_KAKARIKO_VILLAGE) { + Player* player = GET_PLAYER(gPlayState); + if (player->fallDistance > 500 && gSaveContext.health <= 0) { + TimeSplitsSceneSplitEvent(gPlayState->sceneNum); + } + } + }); +} \ No newline at end of file diff --git a/soh/soh/Enhancements/timesplits/TimeSplits.h b/soh/soh/Enhancements/timesplits/TimeSplits.h new file mode 100644 index 00000000000..c208b4c3eae --- /dev/null +++ b/soh/soh/Enhancements/timesplits/TimeSplits.h @@ -0,0 +1,62 @@ +#pragma once +#ifndef TIMESPLITS_H +#define TIMESPLITS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif // TIMESPLITS_H + +#include + +#ifdef __cplusplus +class TimeSplitWindow : public Ship::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override{}; +}; + +typedef enum { + ACTION_SAVE, + ACTION_LOAD, + ACTION_UPDATE, + ACTION_COLLECT +}; + +typedef enum { + SPLIT_ACTIVE, + SPLIT_INACTIVE, + SPLIT_COLLECTED +}; + +typedef enum { + SPLIT_ITEM, + SPLIT_BOSS, + SPLIT_ENTRANCE, + SPLIT_MISC +}; + +typedef struct { + uint32_t splitType; + uint32_t splitID; + std::string splitName; + std::string splitImage; + ImVec4 splitTint; + uint32_t splitTimeCurrent; + uint32_t splitTimeBest; + uint32_t splitTimePreviousBest; + uint32_t splitTimeStatus; + uint32_t splitSkullTokenCount; +} SplitObject; + +#endif diff --git a/soh/soh/SohGui.cpp b/soh/soh/SohGui.cpp index 2d47d2a3895..fa7d192c261 100644 --- a/soh/soh/SohGui.cpp +++ b/soh/soh/SohGui.cpp @@ -108,7 +108,7 @@ namespace SohGui { // MARK: - Delegates - + std::shared_ptr mSohMenuBar; std::shared_ptr mConsoleWindow; @@ -134,6 +134,7 @@ namespace SohGui { std::shared_ptr mItemTrackerSettingsWindow; std::shared_ptr mItemTrackerWindow; std::shared_ptr mRandomizerSettingsWindow; + std::shared_ptr mTimeSplitWindow; std::shared_ptr mAdvancedResolutionSettingsWindow; std::shared_ptr mModalWindow; @@ -206,6 +207,8 @@ namespace SohGui { gui->AddGuiWindow(mRandomizerSettingsWindow); mAdvancedResolutionSettingsWindow = std::make_shared(CVAR_WINDOW("AdvancedResolutionEditor"), "Advanced Resolution Settings", ImVec2(497, 599)); gui->AddGuiWindow(mAdvancedResolutionSettingsWindow); + mTimeSplitWindow = std::make_shared(CVAR_WINDOW("TimeSplitEnabled"), "Time Splits"); + gui->AddGuiWindow(mTimeSplitWindow); mModalWindow = std::make_shared(CVAR_WINDOW("ModalWindow"), "Modal Window"); gui->AddGuiWindow(mModalWindow); mModalWindow->Show(); @@ -238,6 +241,7 @@ namespace SohGui { mStatsWindow = nullptr; mConsoleWindow = nullptr; mSohMenuBar = nullptr; + mTimeSplitWindow = nullptr; mInputViewer = nullptr; mInputViewerSettings = nullptr; } diff --git a/soh/soh/SohGui.hpp b/soh/soh/SohGui.hpp index 9bdedc50464..1c8eecb7996 100644 --- a/soh/soh/SohGui.hpp +++ b/soh/soh/SohGui.hpp @@ -24,6 +24,7 @@ #include "Enhancements/randomizer/randomizer_entrance_tracker.h" #include "Enhancements/randomizer/randomizer_item_tracker.h" #include "Enhancements/randomizer/randomizer_settings_window.h" +#include "Enhancements/timesplits/TimeSplits.h" #include "SohModals.h" #ifdef __cplusplus diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index d30626faea5..0efa3932307 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -44,6 +44,9 @@ // they don't work how I expect them to so I added that because it looked good when I eyeballed it #define FA_ICON_BUTTON_FRAME_PADDING_X(icon) (((optionsButtonSize.x - ImGui::CalcTextSize(icon).x) / 2) + 2.0f) +#include "Enhancements/timesplits/TimeSplits.h" + +extern bool ToggleAltAssetsAtEndOfFrame; extern bool isBetaQuestEnabled; extern "C" PlayState* gPlayState; @@ -559,6 +562,7 @@ void DrawSettingsMenu() { extern std::shared_ptr mAudioEditorWindow; extern std::shared_ptr mCosmeticsEditorWindow; extern std::shared_ptr mGameplayStatsWindow; +extern std::shared_ptr mTimeSplitWindow; void DrawEnhancementsMenu() { if (ImGui::BeginMenu("Enhancements")) @@ -1610,6 +1614,12 @@ void DrawEnhancementsMenu() { mGameplayStatsWindow->ToggleVisibility(); } } + + if (mTimeSplitWindow) { + if (ImGui::Button(GetWindowButtonText("Time Splits", CVarGetInteger(CVAR_WINDOW("gTimeSplitEnabled"), 0)).c_str(), ImVec2(-1.0f, 0.0f))) { + mTimeSplitWindow->ToggleVisibility(); + } + } ImGui::PopStyleVar(3); ImGui::PopStyleColor(1); diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 29f7430ebcd..dabefdffc7f 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -1844,6 +1844,7 @@ void GameplayStats_SetTimestamp(PlayState* play, u8 item) { } gSaveContext.sohStats.itemTimestamp[item] = time; + GameInteractor_ExecuteOnTimestamp(item); } // Gameplay stat tracking: Update time the item was acquired @@ -3318,6 +3319,8 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } } + GameInteractor_ExecuteOnPlayerHealthChange(healthChange); + // "Life=%d *** %d ******" osSyncPrintf(" ライフ=%d *** %d ******\n", gSaveContext.health, healthLevel); diff --git a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c index 59c83fc829e..5333ff7e5f6 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -9,6 +9,8 @@ #include // malloc #include // memcpy +#include "soh/Enhancements/timesplits/TimeSplits.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define LAVA_TEX_WIDTH 32 diff --git a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c index a30091ad3db..83a98c1fa58 100644 --- a/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c +++ b/soh/src/overlays/actors/ovl_Boss_Fd2/z_boss_fd2.c @@ -12,6 +12,8 @@ #include "soh/frame_interpolation.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/timesplits/TimeSplits.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) typedef enum { diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c index 8e2fffe36df..45ff9056553 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon/z_boss_ganon.c @@ -14,6 +14,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include +#include "soh/Enhancements/timesplits/TimeSplits.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) diff --git a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c index 97cc537b4c7..1cbc1176a50 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganon2/z_boss_ganon2.c @@ -10,6 +10,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include +#include "soh/Enhancements/timesplits/TimeSplits.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) diff --git a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c index 03c3cb2923a..ac840e1d181 100644 --- a/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c +++ b/soh/src/overlays/actors/ovl_Boss_Ganondrof/z_boss_ganondrof.c @@ -13,6 +13,8 @@ #include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/timesplits/TimeSplits.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) typedef enum { diff --git a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c index 0644f73a829..8f2e9a0e6d1 100644 --- a/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c +++ b/soh/src/overlays/actors/ovl_Boss_Goma/z_boss_goma.c @@ -7,6 +7,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/timesplits/TimeSplits.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) // IRIS_FOLLOW: gohma looks towards the player (iris rotation) diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index 12dfd415a3b..de0b372862e 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -16,6 +16,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include +#include "soh/Enhancements/timesplits/TimeSplits.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index a739bfd9631..0cd5b19adc6 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -14,6 +14,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/timesplits/TimeSplits.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_DRAGGED_BY_HOOKSHOT) #define vParity actionVar diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index f92526337a9..454097be70e 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -8,6 +8,7 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include +#include "soh/Enhancements/timesplits/TimeSplits.h" #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) diff --git a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c index fc00e641bb3..400b5c26e67 100644 --- a/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c +++ b/soh/src/overlays/actors/ovl_Boss_Va/z_boss_va.c @@ -18,6 +18,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/timesplits/TimeSplits.h" + #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define GET_BODY(this) ((BossVa*)(this)->actor.parent) diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index bddbfa656da..66109ab44aa 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -9013,7 +9013,7 @@ s32 func_80843E64(PlayState* play, Player* this) { Player_RequestRumble(this, impactInfo->rumbleStrength, impactInfo->rumbleDuration, impactInfo->rumbleDecreaseRate, 0); Player_PlaySfx(this, NA_SE_PL_BODY_HIT); func_80832698(this, impactInfo->sfxId); - + return impactIndex + 1; } @@ -15969,7 +15969,7 @@ s32 Player_InflictDamageModified(PlayState* play, s32 damage, u8 modified) { this->stateFlags2 &= ~PLAYER_STATE2_GRABBED_BY_ENEMY; return 1; } - + return 0; }