From f638ae0a050aabeb2244d56011c4abad8575ad8f Mon Sep 17 00:00:00 2001 From: sonoftunk Date: Mon, 21 Nov 2022 21:15:15 -0500 Subject: [PATCH] Complete Refactor of Check Tracker ImGui --- .../cosmetics/CosmeticsEditor.cpp | 8 +- .../Enhancements/randomizer/randomizer.cpp | 8 + soh/soh/Enhancements/randomizer/randomizer.h | 1 + .../randomizer/randomizer_check_objects.cpp | 74 +- .../randomizer/randomizer_check_objects.h | 4 +- .../randomizer/randomizer_check_tracker.cpp | 789 ++++++++++++++++++ .../randomizer/randomizer_check_tracker.h | 15 + .../randomizer/randomizer_item_tracker.cpp | 275 +----- soh/soh/GameMenuBar.cpp | 16 + soh/soh/OTRGlobals.cpp | 2 + 10 files changed, 887 insertions(+), 305 deletions(-) create mode 100644 soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp create mode 100644 soh/soh/Enhancements/randomizer/randomizer_check_tracker.h diff --git a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp index 8c27a914b..97634b483 100644 --- a/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp +++ b/soh/soh/Enhancements/cosmetics/CosmeticsEditor.cpp @@ -38,7 +38,13 @@ const char* RainbowColorCvarList[] = { "gStickTrailTopCol", "gStickTrailBottomCol","gHammerTrailTopCol", "gHammerTrailBottomCol", "gCharged1Col", "gCharged1ColEnv", "gCharged2Col", "gCharged2ColEnv", "gCCFileChoosePrim", "gCCFileChooseTextPrim", "gCCEquipmentsPrim", "gCCItemsPrim", - "gCCMapsPrim", "gCCQuestsPrim", "gCCSavePrim", "gCCGameoverPrim" + "gCCMapsPrim", "gCCQuestsPrim", "gCCSavePrim", "gCCGameoverPrim", + "gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor", + "gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor", + "gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor", + "gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor", + "gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", "gCheckTrackerCheckedMainColor", "gCheckTrackerCheckedExtraColor", + "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", }; const char* MarginCvarList[] { "gHearts", "gHeartsCount", "gMagicBar", "gVSOA", "gBBtn", "gABtn", "gStartBtn", diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index 783745fe6..343253d39 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -2523,6 +2523,14 @@ RandomizerCheck Randomizer::GetCheckFromActor(s16 actorId, s16 sceneNum, s16 act return GetCheckObjectFromActor(actorId, sceneNum, actorParams).rc; } +RandomizerInf Randomizer::GetRandomizerInfFromCheck(RandomizerCheck rc) { + auto rcIt = rcToRandomizerInf.find(rc); + if (rcIt == rcToRandomizerInf.end()) + return RAND_INF_MAX; + + return rcIt->second; +} + RandomizerCheck Randomizer::GetCheckFromRandomizerInf(RandomizerInf randomizerInf) { for (auto const& [key, value] : rcToRandomizerInf) { if (value == randomizerInf) return key; diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index 5149f0c51..dd00a7178 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -65,6 +65,7 @@ class Randomizer { u8 GetRandoSettingValue(RandomizerSettingKey randoSettingKey); RandomizerCheck GetCheckFromActor(s16 actorId, s16 sceneNum, s16 actorParams); RandomizerCheck GetCheckFromRandomizerInf(RandomizerInf randomizerInf); + RandomizerInf GetRandomizerInfFromCheck(RandomizerCheck rc); RandomizerGetData GetRandomizerGetDataFromActor(s16 actorId, s16 sceneNum, s16 actorParams); RandomizerGetData GetRandomizerGetDataFromKnownCheck(RandomizerCheck randomizerCheck); std::string GetChildAltarText() const; diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index 91f70f760..c40affd9e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -3,6 +3,7 @@ #include #include #include "z64.h" +#include "../../OTRGlobals.h" /* typedef struct { @@ -950,35 +951,48 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { } } -void RandomizerCheckObjects::UpdateTrackerImGuiVisibility() { - for (auto& [randomizerCheck, locationIt] : rcObjects) { - locationIt.visibleInImgui = ( - (locationIt.rcArea != RCAREA_INVALID) && // don't show Invalid locations - (locationIt.vOrMQ != RCVORMQ_MQ) && // don't show MQ checks until we support MQ - ((locationIt.rcType != RCTYPE_SHOP) || CVar_GetS32("gRandomizeShopsanity", 0) > 1) && // 1 is the value for "0 random items" for shop - (locationIt.rcType != RCTYPE_GOSSIP_STONE) && // don't show gossip stones (maybe gossipsanity will be a thing eventually?) - (locationIt.rcType != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them - ((locationIt.rcType != RCTYPE_SKULL_TOKEN) || - (CVar_GetS32("gRandomizeShuffleTokens", 0) == 3) || // all tokens - ((CVar_GetS32("gRandomizeShuffleTokens", 0) == 2) && RandomizerCheckObjects::AreaIsOverworld(locationIt.rcArea)) || // overworld tokens - ((CVar_GetS32("gRandomizeShuffleTokens", 0) == 1) && RandomizerCheckObjects::AreaIsDungeon(locationIt.rcArea)) // dungeon tokens - ) && - ((locationIt.rcType != RCTYPE_COW) || CVar_GetS32("gRandomizeShuffleCows", 0)) && - ((locationIt.rcType != RCTYPE_ADULT_TRADE) || CVar_GetS32("gRandomizeShuffleAdultTrade", 0)) && - ((locationIt.rc != RC_KF_KOKIRI_SWORD_CHEST) || CVar_GetS32("gRandomizeShuffleKokiriSword", 0)) && - ((locationIt.rc != RC_HC_MALON_EGG) || CVar_GetS32("gRandomizeShuffleWeirdEgg", 0)) && - ((locationIt.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || CVar_GetS32("gRandomizeShuffleGerudoToken", 0)) && - ((locationIt.rcType != RCTYPE_FROG_SONG) || CVar_GetS32("gRandomizeShuffleFrogSongRupees", 0)) && - ((locationIt.rcType != RCTYPE_MAP_COMPASS) || CVar_GetS32("gRandomizeStartingMapsCompasses", 0) != 1) && // 1 is the value for "vanilla" maps/compasses - ((locationIt.rcType != RCTYPE_SMALL_KEY) || CVar_GetS32("gRandomizeKeysanity", 0) != 1) && // 1 is the value for "vanilla" small keys - ((locationIt.rcType != RCTYPE_GF_KEY) || CVar_GetS32("randoShuffleGerudoFortressKeys", 0) != 0) && // 0 is the value for "vanilla" gf keys - ((locationIt.rcType != RCTYPE_BOSS_KEY) || CVar_GetS32("gRandomizeBossKeysanity", 0) != 1) && // 1 is the value for "vanilla" boss keys - ((locationIt.rcType != RCTYPE_GANON_BOSS_KEY) || CVar_GetS32("gRandomizeShuffleGanonBossKey", 0) != 0) && // 0 is the value for "vanilla" ganon's boss key - ((!RC_IS_CARPENTER(locationIt.rc) && locationIt.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || - (CVar_GetS32("gRandomizeGerudoFortress", 0) == 2 && !RC_IS_CARPENTER(locationIt.rc) && locationIt.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || //2 is the value for "open" gerudo's fortress - (CVar_GetS32("gRandomizeGerudoFortress", 0) == 1 && (locationIt.rc == RC_GF_NORTH_F1_CARPENTER || locationIt.rc == RC_GF_GERUDO_MEMBERSHIP_CARD)) || //1 is the value for "fast" gerudo's fortress - (CVar_GetS32("gRandomizeGerudoFortress", 0) == 0) //0 is the value for "normal" gerudo's fortress - ) +bool RandomizerCheckObjects::IsVisibleInCheckTracker(RandomizerCheckObject rcObj) { + uint8_t gRandomizeShopsanity = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY); + uint8_t gRandomizeShuffleTokens = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS); + uint8_t gRandomizeShuffleCows = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS); + uint8_t gRandomizeShuffleAdultTrade = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE); + uint8_t gRandomizeShuffleKokiriSword = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_KOKIRI_SWORD); + uint8_t gRandomizeShuffleWeirdEgg = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG); + uint8_t gRandomizeShuffleGerudoToken = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD); + uint8_t gRandomizeShuffleFrogSongRupees = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES); + uint8_t gRandomizeStartingMapsCompasses = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_MAPS_COMPASSES); + uint8_t gRandomizeKeysanity = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY); + uint8_t randoShuffleGerudoFortressKeys = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS); + uint8_t gRandomizeBossKeysanity = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY); + uint8_t gRandomizeShuffleGanonBossKey = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY); + uint8_t gRandomizeGerudoFortress = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS); + + return + (rcObj.rcArea != RCAREA_INVALID) && // don't show Invalid locations + (rcObj.vOrMQ != RCVORMQ_MQ) && // don't show MQ checks until we support MQ + ((rcObj.rcType != RCTYPE_SHOP) || gRandomizeShopsanity > 1) && // 1 is the value for "0 random items" for shop + (rcObj.rcType != RCTYPE_GOSSIP_STONE) && // don't show gossip stones (maybe gossipsanity will be a thing eventually?) + (rcObj.rcType != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them + ((rcObj.rcType != RCTYPE_SKULL_TOKEN) || + (gRandomizeShuffleTokens == 3) || // all tokens + ((gRandomizeShuffleTokens == 2) && RandomizerCheckObjects::AreaIsOverworld(rcObj.rcArea)) || // overworld tokens + ((gRandomizeShuffleTokens == 1) && RandomizerCheckObjects::AreaIsDungeon(rcObj.rcArea)) // dungeon tokens + ) && + ((rcObj.rcType != RCTYPE_COW) || gRandomizeShuffleCows) && + ((rcObj.rcType != RCTYPE_ADULT_TRADE) || gRandomizeShuffleAdultTrade) && + ((rcObj.rc != RC_KF_KOKIRI_SWORD_CHEST) || gRandomizeShuffleKokiriSword) && + ((rcObj.rc != RC_HC_MALON_EGG) || gRandomizeShuffleWeirdEgg) && + ((rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || gRandomizeShuffleGerudoToken) && + ((rcObj.rcType != RCTYPE_FROG_SONG) || gRandomizeShuffleFrogSongRupees) && + ((rcObj.rcType != RCTYPE_MAP_COMPASS) || gRandomizeStartingMapsCompasses != 1) && // 1 is the value for "vanilla" maps/compasses + ((rcObj.rcType != RCTYPE_SMALL_KEY) || gRandomizeKeysanity != 1) && // 1 is the value for "vanilla" small keys + ((rcObj.rcType != RCTYPE_GF_KEY) || gRandomizeKeysanity != 0) && // 0 is the value for "vanilla" gf keys + ((rcObj.rcType != RCTYPE_BOSS_KEY) || gRandomizeBossKeysanity != 1) && // 1 is the value for "vanilla" boss keys + ((rcObj.rcType != RCTYPE_GANON_BOSS_KEY) || gRandomizeShuffleGanonBossKey != 0) && // 0 is the value for "vanilla" ganon's boss key + ((!RC_IS_CARPENTER(rcObj.rc) && rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || + (gRandomizeGerudoFortress == 2 && !RC_IS_CARPENTER(rcObj.rc) && rcObj.rc != RC_GF_GERUDO_MEMBERSHIP_CARD) || //2 is the value for "open" gerudo's fortress + (gRandomizeGerudoFortress == 1 && (rcObj.rc == RC_GF_NORTH_F1_CARPENTER || rcObj.rc == RC_GF_GERUDO_MEMBERSHIP_CARD)) || //1 is the value for "fast" gerudo's fortress + (gRandomizeGerudoFortress == 0) //0 is the value for "normal" gerudo's fortress ); - } } + diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.h b/soh/soh/Enhancements/randomizer/randomizer_check_objects.h index 7ad3ce812..c302fc590 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.h +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.h @@ -64,6 +64,7 @@ typedef enum { RCAREA_ICE_CAVERN, RCAREA_GERUDO_TRAINING_GROUND, RCAREA_GANONS_CASTLE, + //If adding any more areas, Check Tracker will need a refactor RCAREA_INVALID } RandomizerCheckArea; @@ -103,4 +104,5 @@ namespace RandomizerCheckObjects { RandomizerCheckArea GetRCAreaBySceneID(SceneID sceneId); void UpdateImGuiVisibility(); void UpdateTrackerImGuiVisibility(); -} + bool IsVisibleInCheckTracker(RandomizerCheckObject rcObj); + } diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp new file mode 100644 index 000000000..2caf62963 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -0,0 +1,789 @@ +#include "randomizer_check_tracker.h" +#include "../../OTRGlobals.h" +#include +#include "../../UIWidgets.hpp" + +#include +#include +#include +#include +#include "3drando/item_location.hpp" + +extern "C" { +#include "variables.h" +#include "functions.h" +#include "macros.h" +extern PlayState* gPlayState; +} +extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); + +void InitializeChecks(); +void UpdateChecks(); +void DrawLocation(RandomizerCheckObject rcObj, RandomizerCheckShow* thisCheckStatus); +void BeginFloatWindows(std::string UniqueName, ImGuiWindowFlags flags = 0); +void EndFloatWindows(); +void UpdateOrdering(bool init = false); +bool ShouldUpdateChecks(); +bool CompareCheckObject(RandomizerCheckObject i, RandomizerCheckObject j); +bool HasItemBeenCollected(RandomizerCheckObject obj); +ImVec4 Color_RGBA8_to_ImVec4(Color_RGBA8& color); +Color_RGBA8 ImVec4_to_Color_RGBA8(ImVec4& color); +RandomizerCheckShow GetCheckStatus(RandomizerCheckObject rcObj, int idx); + + +Color_RGBA8 Color_Bg_Default = { 0, 0, 0, 255 }; // Black +Color_RGBA8 Color_Main_Default = { 255, 255, 255, 255 }; // White +Color_RGBA8 Color_Area_Incomplete_Extra_Default = { 255, 255, 255, 255 }; // White +Color_RGBA8 Color_Area_Complete_Extra_Default = { 255, 255, 255, 255 }; // White +Color_RGBA8 Color_Unchecked_Extra_Default = { 255, 255, 255, 255 }; // White +Color_RGBA8 Color_Skipped_Main_Default = { 160, 160, 160, 255 }; // Grey +Color_RGBA8 Color_Skipped_Extra_Default = { 160, 160, 160, 255 }; // Grey +Color_RGBA8 Color_Seen_Extra_Default = { 255, 255, 255, 255 }; // TODO +Color_RGBA8 Color_Hinted_Extra_Default = { 255, 255, 255, 255 }; // TODO +Color_RGBA8 Color_Checked_Extra_Default = { 255, 255, 255, 255 }; // TODO +Color_RGBA8 Color_Scummed_Extra_Default = { 255, 255, 255, 255 }; // TODO +Color_RGBA8 Color_Saved_Extra_Default = { 0, 185, 0, 255 }; // Green + +ImVec4 Color_Background = { 0, 0, 0, 0 }; + +Color_RGBA8 Color_Area_Incomplete_Main = { 255, 255, 255, 255 }; //White +Color_RGBA8 Color_Area_Incomplete_Extra = { 255, 255, 255, 255 }; //White +Color_RGBA8 Color_Area_Complete_Main = { 255, 255, 255, 255 }; // White +Color_RGBA8 Color_Area_Complete_Extra = { 255, 255, 255, 255 }; // White +Color_RGBA8 Color_Unchecked_Main = { 255, 255, 255, 255 }; //White +Color_RGBA8 Color_Unchecked_Extra = { 255, 255, 255, 255 }; //Useless +Color_RGBA8 Color_Skipped_Main = { 255, 255, 255, 255 }; //Grey +Color_RGBA8 Color_Skipped_Extra = { 255, 255, 255, 255 }; //Grey +Color_RGBA8 Color_Seen_Main = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Seen_Extra = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Hinted_Main = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Hinted_Extra = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Checked_Main = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Checked_Extra = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Scummed_Main = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Scummed_Extra = { 255, 255, 255, 255 }; //TODO +Color_RGBA8 Color_Saved_Main = { 255, 255, 255, 255 }; //White +Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; //Green + +// persistent during gameplay +bool initialized = false; +bool doInitialize = false; +std::map checkStatusMap; +u32 areasFullyChecked = 0; +s8 areaChecksTotal[32]; //| For sorting and showing +s8 areaChecksGotten[32]; //| "Kokiri Forest (4/9)" +std::vector checks; +bool optCollapseAll; // A bool that will collapse all checks once +bool optExpandAll; // A bool that will expand all checks once +s32 lastSaveCount = -1; +RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK; +RandomizerCheckArea previousArea = RCAREA_INVALID; +RandomizerCheckArea currentArea = RCAREA_INVALID; +OSContPad* trackerButtonsPressed; + +std::vector buttons = { BTN_A, BTN_B, BTN_CUP, BTN_CDOWN, BTN_CLEFT, BTN_CRIGHT, BTN_L, + BTN_Z, BTN_R, BTN_START, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; + +void DrawCheckTracker(bool& open) { + if (doInitialize) + InitializeChecks(); + + if (!initialized) + return; + + if (!open) { + CVar_SetS32("gCheckTrackerEnabled", 0); + return; + } + + if (CVar_GetS32("gCheckTrackerWindowType", 1) == 0) { + if (CVar_GetS32("gCheckTrackerShowOnlyPaused", 0) == 1) + if (gPlayState == nullptr || gPlayState->pauseCtx.state == 0) + return; + + if (CVar_GetS32("gCheckTrackerDisplayType", 0) == 1) { + int comboButton1Mask = buttons[CVar_GetS32("gCheckTrackerComboButton1", 6)]; + int comboButton2Mask = buttons[CVar_GetS32("gCheckTrackerComboButton2", 8)]; + bool comboButtonsHeld = trackerButtonsPressed != nullptr && + trackerButtonsPressed[0].button & comboButton1Mask && + trackerButtonsPressed[0].button & comboButton2Mask; + if (!comboButtonsHeld) + return; + } + } + + SceneID sceneId = SCENE_ID_MAX; + if (gPlayState != nullptr) { + sceneId = (SceneID)gPlayState->sceneNum; + currentArea = RandomizerCheckObjects::GetRCAreaBySceneID(sceneId); + } + + bool doAreaScroll = + (currentArea != RCAREA_INVALID && currentArea != previousArea && + sceneId != SCENE_KAKUSIANA && // Don't move for grottos + sceneId != SCENE_YOUSEI_IZUMI_TATE && sceneId != SCENE_YOUSEI_IZUMI_YOKO && sceneId != SCENE_DAIYOUSEI_IZUMI && // Don't move for fairy fountains + sceneId != SCENE_SHOP1 && sceneId != SCENE_SYATEKIJYOU // Don't move for Bazaar/Gallery, as it moves between Kak and Market + ); + previousArea = currentArea; + + //Only update the checks if something has changed + if (ShouldUpdateChecks()) { + UpdateChecks(); + UpdateOrdering(); + } + + BeginFloatWindows("Check Tracker", ImGuiWindowFlags_NoScrollbar); + + //Quick Options +#ifdef __WIIU__ + float headerHeight = 40.0f; +#else + float headerHeight = 20.0f; +#endif + ImVec2 size = ImGui::GetContentRegionMax(); + size.y -= headerHeight; + if (ImGui::BeginTable("Check Tracker", 1, 0, size)) { + ImGui::TableNextRow(0, headerHeight); + ImGui::TableNextColumn(); + UIWidgets::EnhancementCheckbox( + "Show Hidden Items", "gCheckTrackerOptionShowHidden", false, + "When active, items will show hidden checks by default when updated to this state."); + ImGui::SameLine(); + if (ImGui::Button("Expand All")) { + optCollapseAll = false; + optExpandAll = true; + } + ImGui::SameLine(); + if (ImGui::Button("Collapse All")) { + optExpandAll = false; + optCollapseAll = true; + } + UIWidgets::PaddedSeparator(); + } + + //Checks Section Lead-in + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + size = ImGui::GetContentRegionAvail(); + if (!ImGui::BeginTable("CheckTracker##Checks", 1, ImGuiTableFlags_ScrollY, size)) { + ImGui::EndTable(); + EndFloatWindows(); + return; + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + //Prep for loop + bool doDraw = false; + bool thisAreaFullyChecked = false; + bool showHidden = CVar_GetS32("gCheckTrackerOptionShowHidden", 0); + bool hideIncomplete = CVar_GetS32("gCheckTrackerAreaIncompleteHide", 0); + bool hideComplete = CVar_GetS32("gCheckTrackerAreaCompleteHide", 0); + bool collapseLogic; + bool doingCollapseOrExpand = optExpandAll || optCollapseAll; + RandomizerCheckArea lastArea = RCAREA_INVALID; + Color_RGBA8 areaCompleteColor = CVar_GetRGBA("gCheckTrackerAreaMainCompleteColor", Color_Main_Default); + Color_RGBA8 areaIncompleteColor = CVar_GetRGBA("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default); + Color_RGBA8 extraCompleteColor = CVar_GetRGBA("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default); + Color_RGBA8 extraIncompleteColor = CVar_GetRGBA("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default); + Color_RGBA8 mainColor; + Color_RGBA8 extraColor; + std::string stemp; + s32 areaMask = 1; + + // Logic for each check + for (auto& obj : checks) + { + // Second Column mode + // if (twoColumnMode && areaChecksGotten[obj.rcArea] != areaChecksTotal[obj.rcArea]) { + // //TODO draw left column + // ImGui::TableNextColumn(); + // //TODO draw right column + // } + // + + //New Area to be drawn + if (obj.rcArea != lastArea) + { + //Last Area needs to be cleaned up + if (lastArea != RCAREA_INVALID && doDraw) { + ImGui::TreePop(); + UIWidgets::PaddedSeparator(); + } + lastArea = obj.rcArea; + + //Decide if we should skip because of hiding rules + thisAreaFullyChecked = ((areasFullyChecked & areaMask) != 0); + if (!showHidden && ( + hideComplete && thisAreaFullyChecked || + hideIncomplete && !thisAreaFullyChecked + )) { + doDraw = false; + } + else { + //Get the colour for the area + if (thisAreaFullyChecked) { + mainColor = areaCompleteColor; + extraColor = extraCompleteColor; + } else { + mainColor = areaIncompleteColor; + extraColor = extraIncompleteColor; + } + + //Draw the area + collapseLogic = !thisAreaFullyChecked; + if (doingCollapseOrExpand) { + if (optExpandAll) { + collapseLogic = true; + } else if (optCollapseAll) { + collapseLogic = false; + } + } + stemp = RandomizerCheckObjects::GetRCAreaName(obj.rcArea) + "##TreeNode"; + ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(mainColor)); + if (doingCollapseOrExpand) + ImGui::SetNextItemOpen(collapseLogic, ImGuiCond_Always); + else + ImGui::SetNextItemOpen(!thisAreaFullyChecked, ImGuiCond_Once); + doDraw = ImGui::TreeNode(stemp.c_str()); + ImGui::PopStyleColor(); + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(extraColor)); + ImGui::Text("(%d/%d)", areaChecksGotten[obj.rcArea], areaChecksTotal[obj.rcArea]); + ImGui::PopStyleColor(); + + //Keep areas loaded between transitions + if (currentArea == obj.rcArea && doAreaScroll) + ImGui::SetScrollHereY(0.0f); + } + + areaMask <<= 1; + } + + if (doDraw) + DrawLocation(obj, &checkStatusMap.find(obj.rc)->second); + } + + //Clean up last area + if (doDraw) + ImGui::TreePop(); + + ImGui::EndTable(); //Checks Lead-out + ImGui::EndTable(); //Quick Options Lead-out + EndFloatWindows(); + if (doingCollapseOrExpand) { + optCollapseAll = false; + optExpandAll = false; + } +} + +// Windowing stuff +void BeginFloatWindows(std::string UniqueName, ImGuiWindowFlags flags) { + ImGuiWindowFlags windowFlags = flags; + + if (windowFlags == 0) { + windowFlags |= + ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoFocusOnAppearing; + } + + if (!CVar_GetS32("gCheckTrackerWindowType", 0)) { + ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); + windowFlags |= ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar; + + if (!CVar_GetS32("gCheckTrackerHudEditMode", 0)) { + windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; + } + } + ImGui::PushStyleColor(ImGuiCol_WindowBg, Color_Background); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); + ImGui::Begin(UniqueName.c_str(), nullptr, windowFlags); +} +void EndFloatWindows() { + ImGui::PopStyleVar(); + ImGui::PopStyleColor(); + ImGui::PopStyleColor(); + ImGui::End(); +} + + +void InitializeChecks() { + if (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) + return; + + int count = 0; + for (auto& [rcCheck, rcObj] : RandomizerCheckObjects::GetAllRCObjects()) { + if (!RandomizerCheckObjects::IsVisibleInCheckTracker(rcObj)) + continue; + + checks.push_back(rcObj); + checkStatusMap.emplace(rcObj.rc, RCSHOW_UNCHECKED); + count++; + areaChecksTotal[rcObj.rcArea]++; + } + + UpdateChecks(); + UpdateOrdering(true); + doInitialize = false; + initialized = true; +} + +void Teardown() { + initialized = false; + //delete checkStatus; //TODO may cause crashes due to race conditions during a draw cycle, but if you don't have it, then there's a memory leak when switching files + areasFullyChecked = 0; + checks.clear(); + lastSaveCount = -1; + lastLocationChecked = RC_UNKNOWN_CHECK; + for (int i = 0; i < sizeof(areaChecksTotal); i++) { + areaChecksTotal[i] = 0; + areaChecksGotten[i] = 0; + } +} + +bool ShouldUpdateChecks() { + // TODO eventually will need to be hooked into game elements rather than just save file + // TODO, saveCount isn't actually a thing. If not allowed to add it, will need to iterate over save bits and see if any changed + // TODO, or enhance hooks, but that is a LUS change + //return lastSaveCount != gSaveContext.sohStats.saveCount; + + return true; +} + +void UpdateChecks() { + int idx = 0; + RandomizerCheckObject* lastCheck; + RandomizerCheckShow lastStatus; + for (auto& rcObj : checks) { + RandomizerCheckShow* checkStatusPtr = &checkStatusMap.find(rcObj.rc)->second; + lastStatus = *checkStatusPtr; + *checkStatusPtr = GetCheckStatus(rcObj, idx); + + //Update areasFullyChecked + if (lastStatus != *checkStatusPtr) { + if (lastStatus != RCSHOW_CHECKED && lastStatus != RCSHOW_SAVED && (*checkStatusPtr == RCSHOW_CHECKED || *checkStatusPtr == RCSHOW_SAVED)) + areaChecksGotten[rcObj.rcArea]++; + else if ((lastStatus == RCSHOW_CHECKED || lastStatus == RCSHOW_SAVED) && *checkStatusPtr != RCSHOW_CHECKED && *checkStatusPtr != RCSHOW_SAVED) + areaChecksGotten[rcObj.rcArea]--; + + if (areaChecksGotten[rcObj.rcArea] == areaChecksTotal[rcObj.rcArea]) + areasFullyChecked |= (1 << rcObj.rcArea); + else + areasFullyChecked &= (0xFFFFFFFF - (1 << rcObj.rcArea)); + } + + + lastCheck = &rcObj; + + idx++; + } + + //TODO for when using saveCount as a basis + //lastSaveCount = gSaveContext.sohStats.saveCount; +} + +void UpdateOrdering(bool init) { + // Sort the entire thing + if (init) { + std::sort(checks.begin(), checks.end(), CompareCheckObject); + return; + } + //TODO benchmark the full sort over the individual sorts, it might just make sense to always sort all? + + //sort each area individually + int startOffset = 0; + int endOffset = 0; + for (int x = 0; x < sizeof(areaChecksTotal); x++) { + endOffset = startOffset + areaChecksTotal[x]; + std::sort(checks.begin() + startOffset, checks.begin() + endOffset, CompareCheckObject); + startOffset += areaChecksTotal[x]; + } +} + +bool CompareCheckObject(RandomizerCheckObject i, RandomizerCheckObject j) { + if (i.rcArea < j.rcArea) + return true; + else if (i.rcArea > j.rcArea) + return false; + + if (checkStatusMap.find(i.rc)->second < checkStatusMap.find(j.rc)->second) + return true; + else if (checkStatusMap.find(i.rc)->second > checkStatusMap.find(j.rc)->second) + return false; + + if (i.rcShortName < j.rcShortName) + return true; + if (i.rcShortName > j.rcShortName) + return false; + + return false; +} + +RandomizerCheckShow GetCheckStatus(RandomizerCheckObject rcObj, int idx) { + if (HasItemBeenCollected(rcObj)) + return RCSHOW_SAVED; // TODO: use SAVED until we hook into game elements without requiring a save + + //If the status hasn't updated, keep showing as skipped + if (checkStatusMap.find(rcObj.rc)->second == RCSHOW_SKIPPED) + return RCSHOW_SKIPPED; + + return RCSHOW_UNCHECKED; + + // TODO Seen, Hinted, Scummed, saved/checked +} + + +bool HasItemBeenCollected(RandomizerCheckObject obj) { + // TODO doesn't consider vanilla/MQ? + + // TODO move all the code to a static function in item_location? + // return Location(obj.rc)->GetCollectionCheck().IsChecked(gSaveContext); + + ItemLocation* x = Location(obj.rc); + SpoilerCollectionCheck check = x->GetCollectionCheck(); + auto flag = check.flag; + auto scene = check.scene; + auto type = check.type; + + int shift; + int mask; + + switch (type) { + case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED: + return true; + case SpoilerCollectionCheckType::SPOILER_CHK_BIGGORON: + return gSaveContext.bgsFlag & flag; + case SpoilerCollectionCheckType::SPOILER_CHK_CHEST: + return gSaveContext.sceneFlags[scene].chest & (1 << flag); + case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE: + return gSaveContext.sceneFlags[scene].collect & (1 << flag); + case SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT: + case SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM: + case SpoilerCollectionCheckType::SPOILER_CHK_COW: + case SpoilerCollectionCheckType::SPOILER_CHK_SCRUB: + case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF: + return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(obj.rc)); + case SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF: + return gSaveContext.eventChkInf[flag / 16] & (0x01 << flag % 16); + case SpoilerCollectionCheckType::SPOILER_CHK_GERUDO_MEMBERSHIP_CARD: + return CHECK_FLAG_ALL(gSaveContext.eventChkInf[0x09], 0x0F); + case SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA: + return GET_GS_FLAGS(scene) & flag; + case SpoilerCollectionCheckType::SPOILER_CHK_INF_TABLE: + // Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way..... + shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8; + mask = 0x8000 >> shift; + return gSaveContext.infTable[scene] & mask; + case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF: + // Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way..... + shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8; + mask = 0x8000 >> shift; + return gSaveContext.itemGetInf[flag / 16] & mask; + case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS: + return BEANS_BOUGHT >= 10; + case SpoilerCollectionCheckType::SPOILER_CHK_MINIGAME: + if (obj.rc == RC_LH_CHILD_FISHING) + return HIGH_SCORE(HS_FISHING) & 0x400; + if (obj.rc == RC_LH_ADULT_FISHING) + return HIGH_SCORE(HS_FISHING) & 0x800; + case SpoilerCollectionCheckType::SPOILER_CHK_NONE: + return false; + case SpoilerCollectionCheckType::SPOILER_CHK_POE_POINTS: + return gSaveContext.highScores[HS_POE_POINTS] >= 1000; + case SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER: + // Gravedigger has a fix in place that means one of two save locations. Check both. + return (gSaveContext.itemGetInf[1] & 0x1000) || + CVar_GetS32("gGravediggingTourFix", 0) && gSaveContext.sceneFlags[scene].collect & (1 << flag); + default: + return false; + } + return false; +} + +void DrawLocation(RandomizerCheckObject rcObj, RandomizerCheckShow* thisCheckStatus) { + Color_RGBA8 mainColor; + Color_RGBA8 extraColor; + std::string txt; + bool showHidden = CVar_GetS32("gCheckTrackerOptionShowHidden", 0); + + if (*thisCheckStatus == RCSHOW_UNCHECKED) { + if (!showHidden && CVar_GetS32("gCheckTrackerUncheckedHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerUncheckedMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default); + } else if (*thisCheckStatus == RCSHOW_SKIPPED) { + if (!showHidden && CVar_GetS32("gCheckTrackerSkippedHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerSkippedMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default); + } else if (*thisCheckStatus == RCSHOW_SEEN) { + if (!showHidden && CVar_GetS32("gCheckTrackerSeenHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerSeenMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default); + } else if (*thisCheckStatus == RCSHOW_HINTED) { + if (!showHidden && CVar_GetS32("gCheckTrackerHintedHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerHintedMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerHintedExtraColor", Color_Hinted_Extra_Default); + } else if (*thisCheckStatus == RCSHOW_CHECKED) { + if (!showHidden && CVar_GetS32("gCheckTrackerCheckedHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerCheckedMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerCheckedExtraColor", Color_Checked_Extra_Default); + } else if (*thisCheckStatus == RCSHOW_SCUMMED) { + if (!showHidden && CVar_GetS32("gCheckTrackerScummedHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerScummedMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default); + } else if (*thisCheckStatus == RCSHOW_SAVED) { + if (!showHidden && CVar_GetS32("gCheckTrackerSavedHide", 0)) + return; + mainColor = CVar_GetRGBA("gCheckTrackerSavedMainColor", Color_Main_Default); + extraColor = CVar_GetRGBA("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default); + } + + //Main Text + txt = rcObj.rcShortName; + if (lastLocationChecked == rcObj.rc) + txt = "* " + txt; + + //Second Column mode + //TODO + // if (twoColumnMode) ImGui::TableNextColumn(); + + // Draw button - for Skipped/Unchecked only + if (*thisCheckStatus == RCSHOW_UNCHECKED || *thisCheckStatus == RCSHOW_SKIPPED) { + bool skipped = (*thisCheckStatus == RCSHOW_SKIPPED); + if (ImGui::ArrowButton(std::to_string(rcObj.rc).c_str(), skipped ? ImGuiDir_Left : ImGuiDir_Right)) { + if (skipped) + *thisCheckStatus = RCSHOW_UNCHECKED; + else + *thisCheckStatus = RCSHOW_SKIPPED; + } + } else { + ImGui::InvisibleButton("", ImVec2(20.0f, 10.0f)); + } + ImGui::SameLine(); + + //Draw + ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(mainColor)); + ImGui::Text("%s", txt.c_str()); + ImGui::PopStyleColor(); + + //Draw the extra info + if (*thisCheckStatus != RCSHOW_UNCHECKED) { + switch (*thisCheckStatus) { + case RCSHOW_SAVED: + case RCSHOW_CHECKED: + case RCSHOW_SCUMMED: + txt = OTRGlobals::Instance->gRandomizer + ->EnumToSpoilerfileGetName[gSaveContext.itemLocations[rcObj.rc].get.rgID][gSaveContext.language]; + break; + case RCSHOW_SKIPPED: + txt = "Skipped"; //TODO language + break; + case RCSHOW_SEEN: + txt = OTRGlobals::Instance->gRandomizer + ->EnumToSpoilerfileGetName[gSaveContext.itemLocations[rcObj.rc].get.fakeRgID][gSaveContext.language]; + break; + case RCSHOW_HINTED: + txt = "Hints are WIP"; // TODO language + break; + } + + ImGui::PushStyleColor(ImGuiCol_Text, Color_RGBA8_to_ImVec4(extraColor)); + ImGui::SameLine(); + ImGui::Text(" (%s)", txt.c_str()); + ImGui::PopStyleColor(); + } + +} + + +inline void SaveColorToCVar(std::string cvarname, ImVec4 color) { + CVar_SetFloat((cvarname + "R").c_str(), color.x); + CVar_SetFloat((cvarname + "G").c_str(), color.y); + CVar_SetFloat((cvarname + "B").c_str(), color.z); + CVar_SetFloat((cvarname + "A").c_str(), color.w); + SohImGui::RequestCvarSaveOnNextTick(); +} +inline ImVec4 GetColorFromCVar(std::string cvarname) { + float R = CVar_GetFloat((cvarname + "R").c_str(), 255); + float G = CVar_GetFloat((cvarname + "G").c_str(), 255); + float B = CVar_GetFloat((cvarname + "B").c_str(), 255); + float A = CVar_GetFloat((cvarname + "A").c_str(), 255); + return ImVec4(R, G, B, A); +} +inline Color_RGBA8 ImVec4_to_Color_RGBA8(ImVec4& color) { + Color_RGBA8 ret; + ret.r = color.x * 255.0f; + ret.g = color.y * 255.0f; + ret.b = color.z * 255.0f; + ret.a = color.w * 255.0f; + return ret; +} +inline ImVec4 Color_RGBA8_to_ImVec4(Color_RGBA8& color) { + ImVec4 ret; + ret.x = color.r / 255.0f; + ret.y = color.g / 255.0f; + ret.z = color.b / 255.0f; + ret.w = color.a; + return ret; +} + +void ImGuiDrawTwoColorPickerSection(const char* text, const char* cvarMainName, const char* cvarExtraName, + Color_RGBA8& main_color, Color_RGBA8& extra_color, Color_RGBA8& main_default_color, + Color_RGBA8& extra_default_color, const char* cvarHideName) { + Color_RGBA8 cvarMainColor = CVar_GetRGBA(cvarMainName, main_default_color); + Color_RGBA8 cvarExtraColor = CVar_GetRGBA(cvarExtraName, extra_default_color); + main_color = cvarMainColor; + extra_color = cvarExtraColor; + + if (ImGui::CollapsingHeader(text)) { + if (*cvarHideName != '\0') { + std::string label = cvarHideName; + label += "##Hidden"; + ImGui::PushID(label.c_str()); + UIWidgets::EnhancementCheckbox("Hidden", cvarHideName, false, + "When active, checks will hide by default when updated to this state. Can " + "be overriden with the \"Show Hidden Items\" option."); + ImGui::PopID(); + } + if (ImGui::BeginTable(text, 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV | ImGuiTableFlags_Hideable)) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + if (UIWidgets::EnhancementColor("Check", cvarMainName, Color_RGBA8_to_ImVec4(main_color), Color_RGBA8_to_ImVec4(main_default_color))) { + main_color = CVar_GetRGBA(cvarMainName, main_default_color); + }; + ImGui::PopItemWidth(); + + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + if (UIWidgets::EnhancementColor("Details", cvarExtraName, Color_RGBA8_to_ImVec4(extra_color), + Color_RGBA8_to_ImVec4(extra_default_color))) { + main_color = CVar_GetRGBA(cvarExtraName, extra_default_color); + } + ImGui::PopItemWidth(); + + ImGui::EndTable(); + } + } +} + + + +const char* windowType[] = { "Floating", "Window" }; +const char* displayType[] = { "Always", "Combo Button Hold" }; +const char* buttonStrings[] = { "A", "B", "C-Up", "C-Down", "C-Left", "C-Right", "L", + "Z", "R", "Start", "D-Up", "D-Down", "D-Left", "D-Right" }; +void DrawCheckTrackerOptions(bool& open) { + if (!open) { + CVar_SetS32("gCheckTrackerSettingsEnabled", 0); + return; + } + + ImGui::SetNextWindowSize(ImVec2(600, 375), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin("Check Tracker Settings", &open, ImGuiWindowFlags_NoFocusOnAppearing)) { + ImGui::End(); + return; + } + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, { 8.0f, 8.0f }); + ImGui::BeginTable("CheckTrackerSettingsTable", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV); + ImGui::TableSetupColumn("General settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableSetupColumn("Section settings", ImGuiTableColumnFlags_WidthStretch, 200.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("BG Color"); + ImGui::SameLine(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); + if (ImGui::ColorEdit4("BG Color##gCheckTrackerBgColor", (float*)&Color_Background, ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_NoLabel)) { + CVar_SetFloat("gCheckTrackerBgColorR", Color_Background.x); + CVar_SetFloat("gCheckTrackerBgColorG", Color_Background.y); + CVar_SetFloat("gCheckTrackerBgColorB", Color_Background.z); + CVar_SetFloat("gCheckTrackerBgColorA", Color_Background.w); + SohImGui::RequestCvarSaveOnNextTick(); + } + ImGui::PopItemWidth(); + + ImGui::Text("Window Type"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerWindowType", windowType, 2, 1); + + if (CVar_GetS32("gCheckTrackerWindowType", 0) == 0) { + UIWidgets::EnhancementCheckbox("Enable Dragging", "gCheckTrackerHudEditMode", 0); + UIWidgets::EnhancementCheckbox("Only enable while paused", "gCheckTrackerShowOnlyPaused", 0); + ImGui::Text("Display Mode"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerDisplayType", displayType, 2, 0); + if (CVar_GetS32("gCheckTrackerDisplayType", 0) > 0) { + ImGui::Text("Combo Button 2"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerComboButton1", buttonStrings, 14, 6); + ImGui::Text("Combo Button 1"); + ImGui::SameLine(); + UIWidgets::EnhancementCombobox("gCheckTrackerComboButton2", buttonStrings, 14, 8); + } + } + ImGui::TableNextColumn(); + + ImGuiDrawTwoColorPickerSection("Area Incomplete", "gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Main, Color_Area_Incomplete_Extra, Color_Main_Default, Color_Area_Incomplete_Extra_Default, "gCheckTrackerAreaIncompleteHide" ); + ImGuiDrawTwoColorPickerSection("Area Complete", "gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Main, Color_Area_Complete_Extra, Color_Main_Default, Color_Area_Complete_Extra_Default, "gCheckTrackerAreaCompleteHide" ); + ImGuiDrawTwoColorPickerSection("Unchecked", "gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor", Color_Unchecked_Main, Color_Unchecked_Extra, Color_Main_Default, Color_Unchecked_Extra_Default, "gCheckTrackerUncheckedHide" ); + ImGuiDrawTwoColorPickerSection("Skipped", "gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", Color_Skipped_Main, Color_Skipped_Extra, Color_Main_Default, Color_Skipped_Extra_Default, "gCheckTrackerSkippedHide" ); + ImGuiDrawTwoColorPickerSection("Seen (WIP)", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor", Color_Seen_Main, Color_Seen_Extra, Color_Main_Default, Color_Seen_Extra_Default, "gCheckTrackerSeenHide" ); + ImGuiDrawTwoColorPickerSection("Hinted (WIP)", "gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", Color_Hinted_Main, Color_Hinted_Extra, Color_Main_Default, Color_Hinted_Extra_Default, "gCheckTrackerHintedHide" ); + ImGuiDrawTwoColorPickerSection("Checked (WIP)", "gCheckTrackerCheckedMainColor", "gCheckTrackerCheckedExtraColor", Color_Checked_Main, Color_Checked_Extra, Color_Main_Default, Color_Checked_Extra_Default, "gCheckTrackerCheckedHide" ); + ImGuiDrawTwoColorPickerSection("Scummed (WIP)", "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", Color_Scummed_Main, Color_Scummed_Extra, Color_Main_Default, Color_Scummed_Extra_Default, "gCheckTrackerScummedHide" ); + ImGuiDrawTwoColorPickerSection("Saved", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", Color_Saved_Main, Color_Saved_Extra, Color_Main_Default, Color_Saved_Extra_Default, "gCheckTrackerSavedHide" ); + + ImGui::PopStyleVar(1); + ImGui::EndTable(); + ImGui::End(); +} + +void InitCheckTracker() { + SohImGui::AddWindow("Randomizer", "Check Tracker", DrawCheckTracker, CVar_GetS32("gCheckTrackerEnabled", 0) == 1); + SohImGui::AddWindow("Randomizer", "Check Tracker Settings", DrawCheckTrackerOptions); + float trackerBgR = CVar_GetFloat("gCheckTrackerBgColorR", 0); + float trackerBgG = CVar_GetFloat("gCheckTrackerBgColorG", 0); + float trackerBgB = CVar_GetFloat("gCheckTrackerBgColorB", 0); + float trackerBgA = CVar_GetFloat("gCheckTrackerBgColorA", 1); + Color_Background = { trackerBgR, trackerBgG, trackerBgB, trackerBgA }; // Float value, 1 = 255 in rgb value. + Color_Area_Incomplete_Main = CVar_GetRGBA("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default); + Color_Area_Incomplete_Extra = CVar_GetRGBA("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default); + Color_Area_Complete_Main = CVar_GetRGBA("gCheckTrackerAreaMainCompleteColor", Color_Main_Default); + Color_Area_Complete_Extra = CVar_GetRGBA("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default); + Color_Unchecked_Main = CVar_GetRGBA("gCheckTrackerUncheckedMainColor", Color_Main_Default); + Color_Unchecked_Extra = CVar_GetRGBA("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default); + Color_Skipped_Main = CVar_GetRGBA("gCheckTrackerSkippedMainColor", Color_Main_Default); + Color_Skipped_Extra = CVar_GetRGBA("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default); + Color_Seen_Main = CVar_GetRGBA("gCheckTrackerSeenMainColor", Color_Main_Default); + Color_Seen_Extra = CVar_GetRGBA("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default); + Color_Hinted_Main = CVar_GetRGBA("gCheckTrackerHintedMainColor", Color_Main_Default); + Color_Hinted_Extra = CVar_GetRGBA("gCheckTrackerHintedExtraColor", Color_Hinted_Extra_Default); + Color_Checked_Main = CVar_GetRGBA("gCheckTrackerCheckedMainColor", Color_Main_Default); + Color_Checked_Extra = CVar_GetRGBA("gCheckTrackerCheckedExtraColor", Color_Checked_Extra_Default); + Color_Scummed_Main = CVar_GetRGBA("gCheckTrackerScummedMainColor", Color_Main_Default); + Color_Scummed_Extra = CVar_GetRGBA("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default); + Color_Saved_Main = CVar_GetRGBA("gCheckTrackerSavedMainColor", Color_Main_Default); + Color_Saved_Extra = CVar_GetRGBA("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default); + + Ship::RegisterHook([](OSContPad* cont_pad) { + trackerButtonsPressed = cont_pad; + }); + Ship::RegisterHook([](uint32_t fileNum) { + doInitialize = true; + }); + Ship::RegisterHook([](uint32_t fileNum) { + Teardown(); + }); + LocationTable_Init(); +} + diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h new file mode 100644 index 000000000..aeff9a82d --- /dev/null +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.h @@ -0,0 +1,15 @@ +#pragma once + +// Check tracker check visibility categories +typedef enum { + RCSHOW_UNCHECKED, + RCSHOW_SKIPPED, + RCSHOW_SEEN, + RCSHOW_HINTED, + RCSHOW_CHECKED, + RCSHOW_SCUMMED, + RCSHOW_SAVED, +} RandomizerCheckShow; + +void InitCheckTracker(); +void DrawCheckTracker(bool& open); diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index bdcf91e5f..c05496b1e 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -7,10 +7,8 @@ #include #include #include -#include #include #include -#include "3drando/item_location.hpp" extern "C" { #include @@ -32,10 +30,6 @@ void DrawQuest(ItemTrackerItem item); void DrawSong(ItemTrackerItem item); OSContPad* buttonsPressed; -std::set checkedLocations; -std::set skippedLocations; -std::set prevCheckedLocations; -RandomizerCheck lastLocationChecked; bool shouldUpdateVectors = true; @@ -639,261 +633,6 @@ void DrawNotes(bool resizeable = false) { ImGui::EndGroup(); } - -bool HasItemBeenSkipped(RandomizerCheckObject obj) { - return skippedLocations.find(obj.rc) != skippedLocations.end(); -} - -bool HasItemBeenCollected(RandomizerCheckObject obj) { - // TODO doesn't consider vanilla/MQ? - - // TODO move all the code to a static function in item_location - // return Location(obj.rc)->GetCollectionCheck().IsChecked(gSaveContext); - - ItemLocation* x = Location(obj.rc); - SpoilerCollectionCheck check = x->GetCollectionCheck(); - auto flag = check.flag; - auto scene = check.scene; - auto type = check.type; - - int shift; - int mask; - - switch (type) { - case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED: - return true; - case SpoilerCollectionCheckType::SPOILER_CHK_BIGGORON: - return gSaveContext.bgsFlag & flag; - case SpoilerCollectionCheckType::SPOILER_CHK_CHEST: - return gSaveContext.sceneFlags[scene].chest & (1 << flag); - case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE: - return gSaveContext.sceneFlags[scene].collect & (1 << flag); - case SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT: - case SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM: - case SpoilerCollectionCheckType::SPOILER_CHK_COW: - case SpoilerCollectionCheckType::SPOILER_CHK_SCRUB: - case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF: - return Flags_GetRandomizerInf(randomizerFlagLookup[obj.rc]); //TODO randomizer.cpp has rcToRandomizerInf - case SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF: - return gSaveContext.eventChkInf[flag / 16] & (0x01 << flag % 16); - case SpoilerCollectionCheckType::SPOILER_CHK_GERUDO_MEMBERSHIP_CARD: - return CHECK_FLAG_ALL(gSaveContext.eventChkInf[0x09], 0x0F); - case SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA: - return GET_GS_FLAGS(scene) & flag; - case SpoilerCollectionCheckType::SPOILER_CHK_INF_TABLE: - // Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way..... - shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8; - mask = 0x8000 >> shift; - return gSaveContext.infTable[scene] & mask; - case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF: - // Magic to flip an index `flag` to a lookup for 16bit big endian integers. Probably an easier way..... - shift = 7 - (flag % 8) + ((flag % 16) / 8) * 8; - mask = 0x8000 >> shift; - return gSaveContext.itemGetInf[flag / 16] & mask; - case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS: - return BEANS_BOUGHT >= 10; - case SpoilerCollectionCheckType::SPOILER_CHK_MINIGAME: - if (obj.rc == RC_LH_CHILD_FISHING) - return HIGH_SCORE(HS_FISHING) & 0x400; - if (obj.rc == RC_LH_ADULT_FISHING) - return HIGH_SCORE(HS_FISHING) & 0x800; - case SpoilerCollectionCheckType::SPOILER_CHK_NONE: - return false; - case SpoilerCollectionCheckType::SPOILER_CHK_POE_POINTS: - return gSaveContext.highScores[HS_POE_POINTS] >= 1000; - case SpoilerCollectionCheckType::SPOILER_CHK_GRAVEDIGGER: - //Gravedigger has a fix in place that means one of two save locations. Check both. - return (gSaveContext.itemGetInf[1] & 0x1000) || - CVar_GetS32("gGravediggingTourFix", 0) && gSaveContext.sceneFlags[scene].collect & (1 << flag); - default: - return false; - } - return false; -} - -RandomizerCheckArea lastArea = RCAREA_INVALID; -void DrawLocations() { - - if (ImGui::BeginTable("tableRandoChecks", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) { - ImGui::TableSetupColumn("To Check", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableSetupColumn("Checked", ImGuiTableColumnFlags_WidthStretch, 200.0f); - ImGui::TableHeadersRow(); - ImGui::TableNextRow(); - - // COLUMN 1 - TO CHECK LOCATIONS - ImGui::TableNextColumn(); - - static ImGuiTextFilter locationSearch; - locationSearch.Draw(); - - bool lastItemFound = false; - bool doAreaScroll = false; - bool inGame = gPlayState != nullptr && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2; - RandomizerCheckArea currentArea = RCAREA_INVALID; - SceneID sceneId = SCENE_ID_MAX; - if (gPlayState != nullptr) { - sceneId = (SceneID)gPlayState->sceneNum; - currentArea = RandomizerCheckObjects::GetRCAreaBySceneID(sceneId); - } - - ImGui::BeginChild("ChildToCheckLocations", ImVec2(0, -8)); - for (auto& [rcArea, rcObjects] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { - bool hasItems = false; - for (auto& locationIt : rcObjects) { - if (!locationIt.second.visibleInImgui) - continue; - - if (!checkedLocations.count(locationIt.second.rc) && !skippedLocations.count(locationIt.second.rc) && - locationSearch.PassFilter(locationIt.second.rcSpoilerName.c_str())) { - - hasItems = true; - doAreaScroll = - (currentArea != RCAREA_INVALID && sceneId != SCENE_KAKUSIANA && // Don't move for grottos - sceneId != SCENE_YOUSEI_IZUMI_TATE && sceneId != SCENE_YOUSEI_IZUMI_YOKO && // Don't move for fairy fountains - sceneId != SCENE_SHOP1 && sceneId != SCENE_SYATEKIJYOU && // Don't move for Bazaar/Gallery, as it moves between Kak and Market - currentArea != lastArea && currentArea == rcArea); - break; - } - } - - if (hasItems) { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) { - if (doAreaScroll) { - ImGui::SetScrollHereY(0.0f); - doAreaScroll = false; - } - for (auto& locationIt : rcObjects) { - if (!locationIt.second.visibleInImgui) - continue; - - bool checked = HasItemBeenCollected(locationIt.second); - bool skipped = HasItemBeenSkipped(locationIt.second); - - // If the location has its scene flag set - if (inGame && checked) { - // show it as checked - checkedLocations.insert(locationIt.second.rc); - if (skipped) - skippedLocations.erase(locationIt.second.rc); - - if (!lastItemFound && - prevCheckedLocations.find(locationIt.second.rc) == prevCheckedLocations.end()) { - lastItemFound = true; - prevCheckedLocations.insert(locationIt.second.rc); - lastLocationChecked = locationIt.second.rc; - } - } - - if (locationIt.second.visibleInImgui && - !checkedLocations.count(locationIt.second.rc) && !skippedLocations.count(locationIt.second.rc) && - locationSearch.PassFilter(locationIt.second.rcSpoilerName.c_str())) { - - if (ImGui::ArrowButton(std::to_string(locationIt.second.rc).c_str(), ImGuiDir_Right)) { - skippedLocations.insert(locationIt.second.rc); - } else { - ImGui::SameLine(); - ImGui::Text(locationIt.second.rcShortName.c_str()); - } - } - } - ImGui::TreePop(); - } - } - } - ImGui::EndChild(); - - // COLUMN 2 - CHECKED LOCATIONS - doAreaScroll = false; - ImGui::TableNextColumn(); - ImGui::BeginChild("ChildCheckedLocations", ImVec2(0, -8)); - for (auto& [rcArea, rcObjects] : RandomizerCheckObjects::GetAllRCObjectsByArea()) { - bool hasItems = false; - for (auto& locationIt : rcObjects) { - if (!locationIt.second.visibleInImgui) - continue; - - if (checkedLocations.count(locationIt.second.rc) || skippedLocations.count(locationIt.second.rc)) { - hasItems = true; - doAreaScroll = - (currentArea != RCAREA_INVALID && sceneId != SCENE_KAKUSIANA && // Don't move for kakusiana/grottos - sceneId != SCENE_YOUSEI_IZUMI_TATE && sceneId != SCENE_YOUSEI_IZUMI_YOKO && // Don't move for fairy fountains - sceneId != SCENE_SHOP1 && sceneId != SCENE_SYATEKIJYOU && // Don't move for Bazaar/Gallery, as it moves between Kak and Market - currentArea != lastArea && currentArea == rcArea); - break; - } - } - - if (hasItems) { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode(RandomizerCheckObjects::GetRCAreaName(rcArea).c_str())) { - if (doAreaScroll) { - ImGui::SetScrollHereY(0.0f); - doAreaScroll = false; - } - for (auto& locationIt : rcObjects) { - if (!locationIt.second.visibleInImgui) - continue; - - bool checked = HasItemBeenCollected(locationIt.second); - bool skipped = HasItemBeenSkipped(locationIt.second); - - auto elfound = checkedLocations.find(locationIt.second.rc); - auto skfound = skippedLocations.find(locationIt.second.rc); - if (locationIt.second.visibleInImgui && (elfound != checkedLocations.end() || skfound != skippedLocations.end())) { - // If the location has its scene flag set - if (!inGame || (!checked && !skipped)) { - // show it as unchecked - if (!checked && elfound != checkedLocations.end()) - checkedLocations.erase(elfound); - if (!skipped && skfound != skippedLocations.end()) - skippedLocations.erase(skfound); - } else if (skipped && ImGui::ArrowButton(std::to_string(locationIt.second.rc).c_str(), ImGuiDir_Left)) { - if (skipped) - skippedLocations.erase(skfound); - } else if (!skipped) { - float sz = ImGui::GetFrameHeight(); - ImGui::InvisibleButton("", ImVec2(sz, sz)); - } - ImGui::SameLine(); - std::string txt = - (lastLocationChecked == locationIt.second.rc ? "* " : "") + // Indicate the last location checked (before app reset at least) - locationIt.second.rcShortName; - - if (skipped) - ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(160, 160, 160, 255)); - ImGui::Text(txt.c_str()); - if (!skipped) - ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 185, 0, 255)); - - - if (skipped) - txt = "Skipped"; - else - txt = OTRGlobals::Instance->gRandomizer - ->EnumToSpoilerfileGetName[gSaveContext.itemLocations[locationIt.second.rc].get.rgID][gSaveContext.language]; - ImGui::SameLine(); - ImGui::Text("(%s)", txt.c_str()); - ImGui::PopStyleColor(); - } - } - ImGui::TreePop(); - } - } - } - ImGui::EndChild(); - ImGui::EndTable(); - - if (sceneId != SCENE_KAKUSIANA && - sceneId != SCENE_YOUSEI_IZUMI_TATE && - sceneId != SCENE_YOUSEI_IZUMI_YOKO && - sceneId != SCENE_SYATEKIJYOU && - sceneId != SCENE_SHOP1) - lastArea = currentArea; - } -} - - // Windowing stuff ImVec4 ChromaKeyBackground = { 0, 0, 0, 0 }; // Float value, 1 = 255 in rgb value. void BeginFloatingWindows(std::string UniqueName, ImGuiWindowFlags flags = 0) { @@ -1057,6 +796,7 @@ void UpdateVectors() { if (!shouldUpdateVectors) { return; } + dungeonRewards.clear(); dungeonRewards.insert(dungeonRewards.end(), dungeonRewardStones.begin(), dungeonRewardStones.end()); dungeonRewards.insert(dungeonRewards.end(), dungeonRewardMedallions.begin(), dungeonRewardMedallions.end()); @@ -1202,13 +942,6 @@ void DrawItemTracker(bool& open) { DrawNotes(true); EndFloatingWindows(); } - - if (CVar_GetS32("gItemTrackerLocationDisplayType", 0) == 2 && CVar_GetS32("gItemTrackerDisplayType", 0) == 0) { - ImGui::SetNextWindowSize(ImVec2(600, 1000), ImGuiCond_FirstUseEver); - BeginFloatingWindows("Check Tracker", ImGuiWindowFlags_NoFocusOnAppearing); - DrawLocations(); - EndFloatingWindows(); - } } } @@ -1294,7 +1027,6 @@ void DrawItemTrackerOptions(bool& open) { if (CVar_GetS32("gItemTrackerDisplayType", 0) != 1) { LabeledComboBoxRightAligned("Personal notes", "gItemTrackerNotesDisplayType", { "Hidden", "Main Window", "Seperate" }, 0); } - LabeledComboBoxRightAligned("Location Tracker", "gItemTrackerLocationDisplayType", { "Hidden", "Main Window (WIP)", "Separate" }, 0); ImGui::PopStyleVar(1); ImGui::EndTable(); @@ -1325,12 +1057,9 @@ void InitItemTracker() { Ship::RegisterHook([](uint32_t fileNum) { const char* initialTrackerNotes = CVar_GetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), ""); strcpy(itemTrackerNotes.Data, initialTrackerNotes); - RandomizerCheckObjects::UpdateTrackerImGuiVisibility(); }); Ship::RegisterHook([](uint32_t fileNum) { CVar_SetString(("gItemTrackerNotes" + std::to_string(fileNum)).c_str(), ""); SohImGui::RequestCvarSaveOnNextTick(); }); - RandomizerCheckObjects::UpdateTrackerImGuiVisibility(); - LocationTable_Init(); -} +} \ No newline at end of file diff --git a/soh/soh/GameMenuBar.cpp b/soh/soh/GameMenuBar.cpp index 9f82bb250..124c19f75 100644 --- a/soh/soh/GameMenuBar.cpp +++ b/soh/soh/GameMenuBar.cpp @@ -1589,6 +1589,22 @@ namespace GameMenuBar { SohImGui::RequestCvarSaveOnNextTick(); SohImGui::EnableWindow("Item Tracker Settings", CVar_GetS32("gItemTrackerSettingsEnabled", 0)); } + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + if (ImGui::Button(GetWindowButtonText("Check Tracker", CVar_GetS32("gCheckTrackerEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gCheckTrackerEnabled", 0); + CVar_SetS32("gCheckTrackerEnabled", !currentValue); + SohImGui::RequestCvarSaveOnNextTick(); + SohImGui::EnableWindow("Check Tracker", CVar_GetS32("gCheckTrackerEnabled", 0)); + } + ImGui::Dummy(ImVec2(0.0f, 0.0f)); + if (ImGui::Button(GetWindowButtonText("Check Tracker Settings", CVar_GetS32("gCheckTrackerSettingsEnabled", 0)).c_str(), buttonSize)) + { + bool currentValue = CVar_GetS32("gCheckTrackerSettingsEnabled", 0); + CVar_SetS32("gCheckTrackerSettingsEnabled", !currentValue); + SohImGui::RequestCvarSaveOnNextTick(); + SohImGui::EnableWindow("Check Tracker Settings", CVar_GetS32("gCheckTrackerSettingsEnabled", 0)); + } ImGui::PopStyleVar(3); ImGui::PopStyleColor(1); #ifdef ENABLE_CROWD_CONTROL diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index dc9e3c236..d04d75643 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -38,6 +38,7 @@ #include "Enhancements/debugger/debugger.h" #include "Enhancements/randomizer/randomizer.h" #include "Enhancements/randomizer/randomizer_item_tracker.h" +#include "Enhancements/randomizer/randomizer_check_tracker.h" #include "Enhancements/randomizer/3drando/random.hpp" #include "Enhancements/n64_weird_frame_data.inc" #include "frame_interpolation.h" @@ -445,6 +446,7 @@ extern "C" void InitOTR() { Debug_Init(); Rando_Init(); InitItemTracker(); + InitCheckTracker(); OTRExtScanner(); VanillaItemTable_Init();