#include "randomizer_check_tracker.h" #include "randomizer_entrance_tracker.h" #include "randomizer_item_tracker.h" #include "randomizerTypes.h" #include "dungeon.h" #include "../../OTRGlobals.h" #include "../../UIWidgets.hpp" #include #include #include #include #include "location.h" #include "item_location.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "z64item.h" #include "randomizerTypes.h" #include "fishsanity.h" extern "C" { #include "variables.h" #include "functions.h" #include "macros.h" extern PlayState* gPlayState; } extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); extern "C" GetItemEntry ItemTable_RetrieveEntry(s16 modIndex, s16 getItemID); extern std::vector dungeonRewardStones; extern std::vector dungeonRewardMedallions; extern std::vector songItems; extern std::vector equipmentItems; #define RCO_RAORU { RC_GIFT_FROM_SAGES, RCVORMQ_BOTH, RCTYPE_DUNGEON_REWARD, RCAREA_MARKET, ACTOR_ID_MAX, SCENE_ID_MAX, 0x00, GI_NONE, false, "Gift from Raoru", "Gift from Raoru", true }; using json = nlohmann::json; void to_json(json& j, const RandomizerCheckTrackerData& rctd) { j = json { { "status", rctd.status == RCSHOW_COLLECTED ? RCSHOW_SAVED : rctd.status }, { "skipped", rctd.skipped }, { "price", rctd.price }, { "hintItem", rctd.hintItem }}; } void from_json(const json& j, RandomizerCheckTrackerData& rctd) { j.at("status").get_to(rctd.status); j.at("skipped").get_to(rctd.skipped); j.at("hintItem").get_to(rctd.hintItem); j.at("price").get_to(rctd.price); } namespace CheckTracker { // settings bool showShops; bool showOverworldTokens; bool showDungeonTokens; bool showBeans; bool showScrubs; bool showMerchants; bool showBeehives; bool showCows; bool showAdultTrade; bool showKokiriSword; bool showMasterSword; bool showWeirdEgg; bool showGerudoCard; bool showFrogSongRupees; bool showStartingMapsCompasses; bool showKeysanity; bool showGerudoFortressKeys; bool showBossKeysanity; bool showGanonBossKey; bool showOcarinas; bool show100SkullReward; bool showLinksPocket; bool fortressFast; bool fortressNormal; u8 fishsanityMode; u8 fishsanityPondCount; bool fishsanityAgeSplit; // persistent during gameplay bool initialized; bool doAreaScroll; bool previousShowHidden = false; bool hideShopRightChecks = true; bool hideTriforceCompleted = true; bool alwaysShowGS = false; std::map startingShopItem = { { SCENE_KOKIRI_SHOP, RC_KF_SHOP_ITEM_1 }, { SCENE_BAZAAR, RC_MARKET_BAZAAR_ITEM_1 }, { SCENE_POTION_SHOP_MARKET, RC_MARKET_POTION_SHOP_ITEM_1 }, { SCENE_BOMBCHU_SHOP, RC_MARKET_BOMBCHU_SHOP_ITEM_1 }, { SCENE_POTION_SHOP_KAKARIKO, RC_KAK_POTION_SHOP_ITEM_1 }, { SCENE_ZORA_SHOP, RC_ZD_SHOP_ITEM_1 }, { SCENE_GORON_SHOP, RC_GC_SHOP_ITEM_1 } }; std::map DungeonRCAreasBySceneID = { {SCENE_DEKU_TREE, RCAREA_DEKU_TREE}, {SCENE_DODONGOS_CAVERN, RCAREA_DODONGOS_CAVERN}, {SCENE_JABU_JABU, RCAREA_JABU_JABUS_BELLY}, {SCENE_FOREST_TEMPLE, RCAREA_FOREST_TEMPLE}, {SCENE_FIRE_TEMPLE, RCAREA_FIRE_TEMPLE}, {SCENE_WATER_TEMPLE, RCAREA_WATER_TEMPLE}, {SCENE_SHADOW_TEMPLE, RCAREA_SHADOW_TEMPLE}, {SCENE_SPIRIT_TEMPLE, RCAREA_SPIRIT_TEMPLE}, {SCENE_BOTTOM_OF_THE_WELL, RCAREA_BOTTOM_OF_THE_WELL}, {SCENE_ICE_CAVERN, RCAREA_ICE_CAVERN}, {SCENE_GERUDO_TRAINING_GROUND, RCAREA_GERUDO_TRAINING_GROUND}, {SCENE_INSIDE_GANONS_CASTLE, RCAREA_GANONS_CASTLE}, }; std::map> checksByArea; bool areasFullyChecked[RCAREA_INVALID]; u32 areasSpoiled = 0; bool showVOrMQ; s8 areaChecksGotten[RCAREA_INVALID]; //| "Kokiri Forest (4/9)" s8 areaCheckTotals[RCAREA_INVALID]; bool optCollapseAll; // A bool that will collapse all checks once bool optExpandAll; // A bool that will expand all checks once RandomizerCheck lastLocationChecked = RC_UNKNOWN_CHECK; RandomizerCheckArea previousArea = RCAREA_INVALID; RandomizerCheckArea currentArea = RCAREA_INVALID; OSContPad* trackerButtonsPressed; std::unordered_map checkNameOverrides; void BeginFloatWindows(std::string UniqueName, bool& open, ImGuiWindowFlags flags = 0); bool CompareChecks(RandomizerCheck, RandomizerCheck); bool CheckByArea(RandomizerCheckArea); void DrawLocation(RandomizerCheck); void EndFloatWindows(); bool HasItemBeenCollected(RandomizerCheck); void LoadSettings(); void RainbowTick(); void UpdateAreas(RandomizerCheckArea area); void UpdateInventoryChecks(); void UpdateOrdering(RandomizerCheckArea); int sectionId; SceneID DungeonSceneLookupByArea(RandomizerCheckArea area) { switch (area) { case RCAREA_DEKU_TREE: return SCENE_DEKU_TREE; case RCAREA_DODONGOS_CAVERN: return SCENE_DODONGOS_CAVERN; case RCAREA_JABU_JABUS_BELLY: return SCENE_JABU_JABU; case RCAREA_FOREST_TEMPLE: return SCENE_FOREST_TEMPLE; case RCAREA_FIRE_TEMPLE: return SCENE_FIRE_TEMPLE; case RCAREA_WATER_TEMPLE: return SCENE_WATER_TEMPLE; case RCAREA_SPIRIT_TEMPLE: return SCENE_SPIRIT_TEMPLE; case RCAREA_SHADOW_TEMPLE: return SCENE_SHADOW_TEMPLE; case RCAREA_BOTTOM_OF_THE_WELL: return SCENE_BOTTOM_OF_THE_WELL; case RCAREA_ICE_CAVERN: return SCENE_ICE_CAVERN; case RCAREA_GERUDO_TRAINING_GROUND: return SCENE_GERUDO_TRAINING_GROUND; case RCAREA_GANONS_CASTLE: return SCENE_INSIDE_GANONS_CASTLE; default: return SCENE_ID_MAX; } } 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_Collected_Extra_Default = { 242, 101, 34, 255 }; // Orange Color_RGBA8 Color_Scummed_Extra_Default = { 0, 174, 239, 255 }; // Blue Color_RGBA8 Color_Saved_Extra_Default = { 0, 185, 0, 255 }; // Green Color_RGBA8 Color_Background = { 0, 0, 0, 255 }; 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 = { 160, 160, 160, 255 }; // Grey Color_RGBA8 Color_Skipped_Extra = { 160, 160, 160, 255 }; // Grey Color_RGBA8 Color_Seen_Main = { 255, 255, 255, 255 }; // TODO Color_RGBA8 Color_Seen_Extra = { 160, 160, 160, 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_Collected_Main = { 255, 255, 255, 255 }; // White Color_RGBA8 Color_Collected_Extra = { 242, 101, 34, 255 }; // Orange Color_RGBA8 Color_Scummed_Main = { 255, 255, 255, 255 }; // White Color_RGBA8 Color_Scummed_Extra = { 0, 174, 239, 255 }; // Blue Color_RGBA8 Color_Saved_Main = { 255, 255, 255, 255 }; // White Color_RGBA8 Color_Saved_Extra = { 0, 185, 0, 255 }; // Green 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 DefaultCheckData(RandomizerCheck rc) { gSaveContext.checkTrackerData[rc].status = RCSHOW_UNCHECKED; gSaveContext.checkTrackerData[rc].skipped = 0; gSaveContext.checkTrackerData[rc].hintItem = RC_UNKNOWN_CHECK; } void SongFromImpa() { if (IS_RANDO) { if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_CHILD_ZELDA) == RO_GENERIC_ON && IS_RANDO) { if (gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status != RCSHOW_SAVED) { gSaveContext.checkTrackerData[RC_SONG_FROM_IMPA].status = RCSHOW_SAVED; } } } } void GiftFromSages() { if (!IS_RANDO) { DefaultCheckData(RC_GIFT_FROM_SAGES); } } std::vector checks; // Function for adding Link's Pocket check void LinksPocket() { if (IS_RANDO) { if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_DUNGEON_REWARDS) == RO_DUNGEON_REWARDS_END_OF_DUNGEON) { DefaultCheckData(RC_LINKS_POCKET); gSaveContext.checkTrackerData[RC_LINKS_POCKET].status = RCSHOW_SAVED; } } } void TrySetAreas() { if (checksByArea.empty()) { for (int i = RCAREA_KOKIRI_FOREST; i < RCAREA_INVALID; i++) { checksByArea.emplace(static_cast(i), std::vector()); } } } void RecalculateAreaTotals() { for (auto [rcArea, checks] : checksByArea) { if (rcArea == RCAREA_INVALID) { return; } areaChecksGotten[rcArea] = 0; areaCheckTotals[rcArea] = 0; for (auto rc : checks) { if (!IsVisibleInCheckTracker(rc)) { continue; } areaCheckTotals[rcArea]++; if (gSaveContext.checkTrackerData[rc].skipped || gSaveContext.checkTrackerData[rc].status == RCSHOW_COLLECTED || gSaveContext.checkTrackerData[rc].status == RCSHOW_SAVED) { areaChecksGotten[rcArea]++; } } } } void SetCheckCollected(RandomizerCheck rc) { gSaveContext.checkTrackerData[rc].status = RCSHOW_COLLECTED; Rando::Location* loc = Rando::StaticData::GetLocation(rc); if (IsVisibleInCheckTracker(rc)) { if (!gSaveContext.checkTrackerData[rc].skipped) { areaChecksGotten[loc->GetArea()]++; } else { gSaveContext.checkTrackerData[rc].skipped = false; } } SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); doAreaScroll = true; UpdateOrdering(loc->GetArea()); UpdateInventoryChecks(); } bool IsAreaScene(SceneID sceneNum) { switch (sceneNum) { case SCENE_HYRULE_FIELD: case SCENE_KAKARIKO_VILLAGE: case SCENE_GRAVEYARD: case SCENE_ZORAS_RIVER: case SCENE_KOKIRI_FOREST: case SCENE_SACRED_FOREST_MEADOW: case SCENE_LAKE_HYLIA: case SCENE_ZORAS_DOMAIN: case SCENE_ZORAS_FOUNTAIN: case SCENE_GERUDO_VALLEY: case SCENE_LOST_WOODS: case SCENE_DESERT_COLOSSUS: case SCENE_GERUDOS_FORTRESS: case SCENE_HAUNTED_WASTELAND: case SCENE_HYRULE_CASTLE: case SCENE_DEATH_MOUNTAIN_TRAIL: case SCENE_DEATH_MOUNTAIN_CRATER: case SCENE_GORON_CITY: case SCENE_LON_LON_RANCH: case SCENE_DEKU_TREE: case SCENE_DODONGOS_CAVERN: case SCENE_JABU_JABU: case SCENE_FOREST_TEMPLE: case SCENE_FIRE_TEMPLE: case SCENE_WATER_TEMPLE: case SCENE_SPIRIT_TEMPLE: case SCENE_SHADOW_TEMPLE: case SCENE_BOTTOM_OF_THE_WELL: case SCENE_ICE_CAVERN: case SCENE_GERUDO_TRAINING_GROUND: case SCENE_GANONS_TOWER: case SCENE_INSIDE_GANONS_CASTLE: case SCENE_BACK_ALLEY_DAY: case SCENE_BACK_ALLEY_NIGHT: case SCENE_MARKET_DAY: case SCENE_MARKET_NIGHT: case SCENE_MARKET_RUINS: return true; default: return false; } } RandomizerCheckArea AreaFromEntranceGroup[] = { RCAREA_INVALID, RCAREA_KOKIRI_FOREST, RCAREA_LOST_WOODS, RCAREA_SACRED_FOREST_MEADOW, RCAREA_KAKARIKO_VILLAGE, RCAREA_GRAVEYARD, RCAREA_DEATH_MOUNTAIN_TRAIL, RCAREA_DEATH_MOUNTAIN_CRATER, RCAREA_GORON_CITY, RCAREA_ZORAS_RIVER, RCAREA_ZORAS_DOMAIN, RCAREA_ZORAS_FOUNTAIN, RCAREA_HYRULE_FIELD, RCAREA_LON_LON_RANCH, RCAREA_LAKE_HYLIA, RCAREA_GERUDO_VALLEY, RCAREA_WASTELAND, RCAREA_MARKET, RCAREA_HYRULE_CASTLE, }; RandomizerCheckArea GetCheckArea() { auto scene = static_cast(gPlayState->sceneNum); bool grottoScene = (scene == SCENE_GROTTOS || scene == SCENE_FAIRYS_FOUNTAIN); const EntranceData* ent = GetEntranceData(grottoScene ? ENTRANCE_RANDO_GROTTO_EXIT_START + GetCurrentGrottoId() : gSaveContext.entranceIndex); RandomizerCheckArea area = RCAREA_INVALID; if (ent != nullptr && !IsAreaScene(scene) && ent->type != ENTRANCE_TYPE_DUNGEON) { if (ent->source == "Desert Colossus" || ent->destination == "Desert Colossus") { area = RCAREA_DESERT_COLOSSUS; } else if (ent->source == "Gerudo Fortress" || ent->destination == "Gerudo Fortress") { area = RCAREA_GERUDO_FORTRESS; } else { area = AreaFromEntranceGroup[ent->dstGroup]; } } if (area == RCAREA_INVALID) { if (grottoScene && (GetCurrentGrottoId() == -1) && (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GROTTO_ENTRANCES) == RO_GENERIC_OFF)) { area = previousArea; } else { area = RandomizerCheckObjects::GetRCAreaBySceneID(scene); } } return area; } bool vector_contains_scene(std::vector vec, const int16_t scene) { return std::any_of(vec.begin(), vec.end(), [&](const auto& x) { return x == scene; }); } std::vector skipScenes = {SCENE_GANON_BOSS, SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR, SCENE_GANON_BOSS, SCENE_INSIDE_GANONS_CASTLE_COLLAPSE, SCENE_GANONS_TOWER_COLLAPSE_INTERIOR}; void ClearAreaChecksAndTotals() { for (auto& [rcArea, vec] : checksByArea) { vec.clear(); areaChecksGotten[rcArea] = 0; areaCheckTotals[rcArea] = 0; } } void SetShopSeen(uint32_t sceneNum, bool prices) { RandomizerCheck start = startingShopItem.find(sceneNum)->second; if (sceneNum == SCENE_POTION_SHOP_KAKARIKO && !LINK_IS_ADULT) { return; } if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && sceneNum == SCENE_BAZAAR) { start = RC_KAK_BAZAAR_ITEM_1; } bool statusChanged = false; for (int i = start; i < start + 8; i++) { if (gSaveContext.checkTrackerData[i].status == RCSHOW_UNCHECKED) { gSaveContext.checkTrackerData[i].status = RCSHOW_SEEN; statusChanged = true; } } if (statusChanged) { SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); } } bool HasItemBeenCollected(RandomizerCheck rc) { if (gPlayState == nullptr) { return false; } Rando::Location* x = Rando::StaticData::GetLocation(rc); Rando::SpoilerCollectionCheck check = x->GetCollectionCheck(); auto flag = check.flag; auto scene = check.scene; auto type = check.type; switch (type) { case SpoilerCollectionCheckType::SPOILER_CHK_ALWAYS_COLLECTED: return true; case SpoilerCollectionCheckType::SPOILER_CHK_CHEST: return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.chest & (1 << flag)) || gSaveContext.sceneFlags[scene].chest & (1 << flag); case SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE: return (gPlayState->sceneNum == scene && gPlayState->actorCtx.flags.collect & (1 << flag)) || 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_FISH: case SpoilerCollectionCheckType::SPOILER_CHK_SCRUB: case SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF: case SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD: return Flags_GetRandomizerInf(OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(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: return gSaveContext.infTable[scene] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag); case SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF: return gSaveContext.itemGetInf[flag / 16] & INDEX_TO_16BIT_LITTLE_ENDIAN_BITMASK(flag); case SpoilerCollectionCheckType::SPOILER_CHK_MAGIC_BEANS: return BEANS_BOUGHT >= 10; case SpoilerCollectionCheckType::SPOILER_CHK_NONE: return false; 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) || // vanilla flag ((IS_RANDO || CVarGetInteger("gGravediggingTourFix", 0)) && gSaveContext.sceneFlags[scene].collect & (1 << flag) || (gPlayState->actorCtx.flags.collect & (1 << flag))); // rando/fix flag default: return false; } return false; } void CheckTrackerLoadGame(int32_t fileNum) { LoadSettings(); TrySetAreas(); for (auto& entry : Rando::StaticData::GetLocationTable()) { RandomizerCheck rc = entry.GetRandomizerCheck(); RandomizerCheckTrackerData rcTrackerData = gSaveContext.checkTrackerData[rc]; if (rc == RC_UNKNOWN_CHECK || rc == RC_MAX || rc == RC_LINKS_POCKET || !Rando::StaticData::GetLocation(rc) != RC_UNKNOWN_CHECK) { continue; } Rando::Location* entry2; if (rc == RC_GIFT_FROM_SAGES && !IS_RANDO) { entry2 = Rando::StaticData::GetLocation(rc); } else { entry2 = Rando::StaticData::GetLocation(rc); } checksByArea.find(entry2->GetArea())->second.push_back(entry2->GetRandomizerCheck()); if (IsVisibleInCheckTracker(entry2->GetRandomizerCheck())) { areaCheckTotals[entry2->GetArea()]++; if (rcTrackerData.status == RCSHOW_SAVED || rcTrackerData.skipped) { areaChecksGotten[entry2->GetArea()]++; } } if (areaChecksGotten[entry2->GetArea()] != 0 || RandomizerCheckObjects::AreaIsOverworld(entry2->GetArea())) { areasSpoiled |= (1 << entry2->GetArea()); } // Create check name overrides for child pond fish if age split is disabled if (fishsanityMode != RO_FISHSANITY_OFF && fishsanityMode != RO_FISHSANITY_OVERWORLD && entry.GetRCType() == RCTYPE_FISH && entry.GetScene() == SCENE_FISHING_POND && entry.GetActorParams() != 116 && !fishsanityAgeSplit) { if (entry.GetShortName().starts_with("Child")) { checkNameOverrides[rc] = entry.GetShortName().substr(6); } } } if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING && IS_RANDO) { s8 startingAge = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_STARTING_AGE); RandomizerCheckArea startingArea; switch (startingAge) { case RO_AGE_CHILD: startingArea = RCAREA_KOKIRI_FOREST; break; case RO_AGE_ADULT: startingArea = RCAREA_MARKET; break; default: startingArea = RCAREA_KOKIRI_FOREST; break; } checksByArea.find(startingArea)->second.push_back(RC_LINKS_POCKET); areaChecksGotten[startingArea]++; areaCheckTotals[startingArea]++; } showVOrMQ = (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_RANDOM_NUMBER || (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SET_NUMBER && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) < 12)); LinksPocket(); SongFromImpa(); GiftFromSages(); initialized = true; UpdateAllOrdering(); UpdateInventoryChecks(); } void CheckTrackerShopSlotChange(uint8_t cursorSlot, int16_t basePrice) { if (gPlayState->sceneNum == SCENE_HAPPY_MASK_SHOP) { // Happy Mask Shop is not used in rando, so is not tracked return; } auto slot = startingShopItem.find(gPlayState->sceneNum)->second + cursorSlot; if (GetCheckArea() == RCAREA_KAKARIKO_VILLAGE && gPlayState->sceneNum == SCENE_BAZAAR) { slot = RC_KAK_BAZAAR_ITEM_1 + cursorSlot; } auto status = gSaveContext.checkTrackerData[slot].status; if (status == RCSHOW_SEEN) { gSaveContext.checkTrackerData[slot].status = RCSHOW_IDENTIFIED; gSaveContext.checkTrackerData[slot].price = basePrice; SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); } } void CheckTrackerTransition(uint32_t sceneNum) { if (!GameInteractor::IsSaveLoaded()) { return; } doAreaScroll = true; previousArea = currentArea; currentArea = GetCheckArea(); switch (sceneNum) { case SCENE_KOKIRI_SHOP: case SCENE_BAZAAR: case SCENE_POTION_SHOP_MARKET: case SCENE_BOMBCHU_SHOP: case SCENE_POTION_SHOP_KAKARIKO: case SCENE_GORON_SHOP: case SCENE_ZORA_SHOP: SetShopSeen(sceneNum, false); break; } } void CheckTrackerFrame() { if (!GameInteractor::IsSaveLoaded()) { return; } // TODO: Move to OnAmmoChange hook once it gets added. if (gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_COLLECTED && gSaveContext.checkTrackerData[RC_ZR_MAGIC_BEAN_SALESMAN].status != RCSHOW_SAVED) { if (BEANS_BOUGHT >= 10) { SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN); } } } void CheckTrackerItemReceive(GetItemEntry giEntry) { if (!GameInteractor::IsSaveLoaded() || vector_contains_scene(skipScenes, gPlayState->sceneNum)) { return; } auto scene = static_cast(gPlayState->sceneNum); // Vanilla special item checks if (!IS_RANDO) { if (giEntry.itemId == ITEM_SHIELD_DEKU) { SetCheckCollected(RC_KF_SHOP_ITEM_1); return; }else if (giEntry.itemId == ITEM_KOKIRI_EMERALD) { SetCheckCollected(RC_QUEEN_GOHMA); return; } else if (giEntry.itemId == ITEM_GORON_RUBY) { SetCheckCollected(RC_KING_DODONGO); return; } else if (giEntry.itemId == ITEM_ZORA_SAPPHIRE) { SetCheckCollected(RC_BARINADE); return; } else if (giEntry.itemId == ITEM_MEDALLION_FOREST) { SetCheckCollected(RC_PHANTOM_GANON); return; } else if (giEntry.itemId == ITEM_MEDALLION_FIRE) { SetCheckCollected(RC_VOLVAGIA); return; } else if (giEntry.itemId == ITEM_MEDALLION_WATER) { SetCheckCollected(RC_MORPHA); return; } else if (giEntry.itemId == ITEM_MEDALLION_SHADOW) { SetCheckCollected(RC_BONGO_BONGO); return; } else if (giEntry.itemId == ITEM_MEDALLION_SPIRIT) { SetCheckCollected(RC_TWINROVA); return; } else if (giEntry.itemId == ITEM_MEDALLION_LIGHT) { SetCheckCollected(RC_GIFT_FROM_SAGES); return; } else if (giEntry.itemId == ITEM_SONG_EPONA) { SetCheckCollected(RC_SONG_FROM_MALON); return; } else if (giEntry.itemId == ITEM_SONG_SARIA) { SetCheckCollected(RC_SONG_FROM_SARIA); return; } else if (giEntry.itemId == ITEM_BEAN) { SetCheckCollected(RC_ZR_MAGIC_BEAN_SALESMAN); return; } else if (giEntry.itemId == ITEM_BRACELET) { SetCheckCollected(RC_GC_DARUNIAS_JOY); return; }/* else if (giEntry.itemId == ITEM_SONG_SUN) { SetCheckCollected(RC_SONG_FROM_ROYAL_FAMILYS_TOMB); return; } else if (giEntry.itemId == ITEM_SONG_TIME) { SetCheckCollected(RC_SONG_FROM_OCARINA_OF_TIME); return; } else if (giEntry.itemId == ITEM_SONG_STORMS) { SetCheckCollected(RC_SONG_FROM_WINDMILL); return; } else if (giEntry.itemId == ITEM_SONG_MINUET) { SetCheckCollected(RC_SHEIK_IN_FOREST); return; } else if (giEntry.itemId == ITEM_SONG_BOLERO) { SetCheckCollected(RC_SHEIK_IN_CRATER); return; } else if (giEntry.itemId == ITEM_SONG_SERENADE) { SetCheckCollected(RC_SHEIK_IN_ICE_CAVERN); return; } else if (giEntry.itemId == ITEM_SONG_NOCTURNE) { SetCheckCollected(RC_SHEIK_IN_KAKARIKO); return; } else if (giEntry.itemId == ITEM_SONG_REQUIEM) { SetCheckCollected(RC_SHEIK_AT_COLOSSUS); return; } else if (giEntry.itemId == ITEM_SONG_PRELUDE) { SetCheckCollected(RC_SHEIK_AT_TEMPLE); return; }*/ } } void CheckTrackerSceneFlagSet(int16_t sceneNum, int16_t flagType, int32_t flag) { if (flagType != FLAG_SCENE_TREASURE && flagType != FLAG_SCENE_COLLECTIBLE) { return; } if (sceneNum == SCENE_GRAVEYARD && flag == 0x19 && flagType == FLAG_SCENE_COLLECTIBLE) { // Gravedigging tour special case SetCheckCollected(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR); return; } for (auto& loc : Rando::StaticData::GetLocationTable()) { if (!IsVisibleInCheckTracker(loc.GetRandomizerCheck())) { continue; } SpoilerCollectionCheckType checkMatchType = flagType == FLAG_SCENE_TREASURE ? SpoilerCollectionCheckType::SPOILER_CHK_CHEST : SpoilerCollectionCheckType::SPOILER_CHK_COLLECTABLE; Rando::SpoilerCollectionCheck scCheck = loc.GetCollectionCheck(); if (scCheck.scene == sceneNum && scCheck.flag == flag && scCheck.type == checkMatchType) { SetCheckCollected(loc.GetRandomizerCheck()); return; } } } void CheckTrackerFlagSet(int16_t flagType, int32_t flag) { SpoilerCollectionCheckType checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_NONE; switch (flagType) { case FLAG_GS_TOKEN: checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA; break; case FLAG_EVENT_CHECK_INF: if ((flag == EVENTCHKINF_CARPENTERS_FREE(0) || flag == EVENTCHKINF_CARPENTERS_FREE(1) || flag == EVENTCHKINF_CARPENTERS_FREE(2) || flag == EVENTCHKINF_CARPENTERS_FREE(3)) && GET_EVENTCHKINF_CARPENTERS_FREE_ALL()) { SetCheckCollected(RC_GF_GERUDO_MEMBERSHIP_CARD); return; } checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_EVENT_CHK_INF; break; case FLAG_INF_TABLE: if (flag == INFTABLE_190) { SetCheckCollected(RC_GF_HBA_1000_POINTS); return; } else if (flag == INFTABLE_11E) { SetCheckCollected(RC_GC_ROLLING_GORON_AS_CHILD); return; } else if (flag == INFTABLE_GORON_CITY_DOORS_UNLOCKED) { SetCheckCollected(RC_GC_ROLLING_GORON_AS_ADULT); return; } else if (flag == INFTABLE_139) { SetCheckCollected(RC_ZD_KING_ZORA_THAWED); return; } else if (flag == INFTABLE_191) { SetCheckCollected(RC_MARKET_LOST_DOG); return; } if (!IS_RANDO) { if (flag == INFTABLE_192) { SetCheckCollected(RC_LW_DEKU_SCRUB_NEAR_BRIDGE); return; } else if (flag == INFTABLE_193) { SetCheckCollected(RC_LW_DEKU_SCRUB_GROTTO_FRONT); return; } } break; case FLAG_ITEM_GET_INF: if (!IS_RANDO) { if (flag == ITEMGETINF_OBTAINED_STICK_UPGRADE_FROM_STAGE) { SetCheckCollected(RC_DEKU_THEATER_SKULL_MASK); return; } else if (flag == ITEMGETINF_OBTAINED_NUT_UPGRADE_FROM_STAGE) { SetCheckCollected(RC_DEKU_THEATER_MASK_OF_TRUTH); return; } else if (flag == ITEMGETINF_0B) { SetCheckCollected(RC_HF_DEKU_SCRUB_GROTTO); return; } } checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_ITEM_GET_INF; break; case FLAG_RANDOMIZER_INF: checkMatchType = SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF; break; } if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_NONE) { return; } for (auto& loc : Rando::StaticData::GetLocationTable()) { if ((!IS_RANDO && ((loc.GetQuest() == RCQUEST_MQ && !IS_MASTER_QUEST) || (loc.GetQuest() == RCQUEST_VANILLA && IS_MASTER_QUEST))) || (IS_RANDO && !(OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene()) == nullptr) && ((OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene())->IsMQ() && loc.GetQuest() == RCQUEST_VANILLA) || OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc.GetScene())->IsVanilla() && loc.GetQuest() == RCQUEST_MQ))) { continue; } Rando::SpoilerCollectionCheck scCheck = loc.GetCollectionCheck(); SpoilerCollectionCheckType scCheckType = scCheck.type; if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF && (scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MERCHANT || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SHOP_ITEM || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_COW || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_FISH || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_SCRUB || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_MASTER_SWORD || scCheckType == SpoilerCollectionCheckType::SPOILER_CHK_RANDOMIZER_INF)) { if (flag == OTRGlobals::Instance->gRandomizer->GetRandomizerInfFromCheck(loc.GetRandomizerCheck())) { SetCheckCollected(loc.GetRandomizerCheck()); return; } continue; } int16_t checkFlag = scCheck.flag; if (checkMatchType == SpoilerCollectionCheckType::SPOILER_CHK_GOLD_SKULLTULA) { checkFlag = loc.GetActorParams(); } if (checkFlag == flag && scCheck.type == checkMatchType) { SetCheckCollected(loc.GetRandomizerCheck()); return; } } } void InitTrackerData(bool isDebug) { TrySetAreas(); for (auto& loc : Rando::StaticData::GetLocationTable()) { if (loc.GetRandomizerCheck() != RC_UNKNOWN_CHECK && loc.GetRandomizerCheck() != RC_MAX) { DefaultCheckData(loc.GetRandomizerCheck()); } } UpdateAllOrdering(); } void SaveTrackerData(SaveContext* saveContext, int sectionID, bool gameSave) { SaveManager::Instance->SaveArray("checks", ARRAY_COUNT(saveContext->checkTrackerData), [&](size_t i) { if (saveContext->checkTrackerData[i].status == RCSHOW_COLLECTED) { if (gameSave) { gSaveContext.checkTrackerData[i].status = saveContext->checkTrackerData[i].status = RCSHOW_SAVED; UpdateAllOrdering(); UpdateInventoryChecks(); } else { saveContext->checkTrackerData[i].status = RCSHOW_SCUMMED; } } SaveManager::Instance->SaveStruct("", [&]() { SaveManager::Instance->SaveData("status", saveContext->checkTrackerData[i].status); SaveManager::Instance->SaveData("skipped", saveContext->checkTrackerData[i].skipped); SaveManager::Instance->SaveData("price", saveContext->checkTrackerData[i].price); SaveManager::Instance->SaveData("hintItem", saveContext->checkTrackerData[i].hintItem); }); }); } void SaveFile(SaveContext* saveContext, int sectionID, bool fullSave) { SaveTrackerData(saveContext, sectionID, fullSave); } void LoadFile() { SaveManager::Instance->LoadArray("checks", RC_MAX, [](size_t i) { SaveManager::Instance->LoadStruct("", [&]() { SaveManager::Instance->LoadData("status", gSaveContext.checkTrackerData[i].status); SaveManager::Instance->LoadData("skipped", gSaveContext.checkTrackerData[i].skipped); SaveManager::Instance->LoadData("price", gSaveContext.checkTrackerData[i].price); SaveManager::Instance->LoadData("hintItem", gSaveContext.checkTrackerData[i].hintItem); }); }); } void Teardown() { initialized = false; ClearAreaChecksAndTotals(); checksByArea.clear(); areasSpoiled = 0; lastLocationChecked = RC_UNKNOWN_CHECK; } void UpdateCheck(uint32_t check, RandomizerCheckTrackerData data) { auto area = Rando::StaticData::GetLocation(static_cast(check))->GetArea(); if ((!gSaveContext.checkTrackerData[check].skipped && data.skipped) || ((gSaveContext.checkTrackerData[check].status != RCSHOW_COLLECTED && gSaveContext.checkTrackerData[check].status != RCSHOW_SAVED) && (data.status == RCSHOW_COLLECTED || data.status == RCSHOW_SAVED))) { areaChecksGotten[area]++; } else if ((gSaveContext.checkTrackerData[check].skipped && !data.skipped) || ((gSaveContext.checkTrackerData[check].status == RCSHOW_COLLECTED || gSaveContext.checkTrackerData[check].status == RCSHOW_SAVED) && (data.status != RCSHOW_COLLECTED && data.status != RCSHOW_SAVED))) { areaChecksGotten[area]--; } gSaveContext.checkTrackerData[check] = data; UpdateOrdering(area); } void CheckTrackerWindow::DrawElement() { ImGui::SetNextWindowSize(ImVec2(400, 540), ImGuiCond_FirstUseEver); if (!initialized && (gPlayState == nullptr || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2)) { return; } if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { if (CVarGetInteger("gCheckTrackerShowOnlyPaused", 0) && (gPlayState == nullptr || gPlayState->pauseCtx.state == 0)) { return; } if (CVarGetInteger("gCheckTrackerDisplayType", TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) { int comboButton1Mask = buttons[CVarGetInteger("gCheckTrackerComboButton1", TRACKER_COMBO_BUTTON_L)]; int comboButton2Mask = buttons[CVarGetInteger("gCheckTrackerComboButton2", TRACKER_COMBO_BUTTON_R)]; OSContPad* trackerButtonsPressed = LUS::Context::GetInstance()->GetControlDeck()->GetPads(); bool comboButtonsHeld = trackerButtonsPressed != nullptr && trackerButtonsPressed[0].button & comboButton1Mask && trackerButtonsPressed[0].button & comboButton2Mask; if (!comboButtonsHeld) { return; } } } BeginFloatWindows("Check Tracker", mIsVisible, ImGuiWindowFlags_NoScrollbar); if (!GameInteractor::IsSaveLoaded()) { ImGui::Text("Waiting for file load..."); //TODO Language EndFloatWindows(); return; } SceneID sceneId = SCENE_ID_MAX; if (gPlayState != nullptr) { sceneId = (SceneID)gPlayState->sceneNum; } areasSpoiled |= (1 << currentArea); //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)) { EndFloatWindows(); return; } 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."); UIWidgets::PaddedSeparator(); if (ImGui::Button("Expand All")) { optCollapseAll = false; optExpandAll = true; doAreaScroll = 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 RainbowTick(); bool doDraw = false; bool thisAreaFullyChecked = false; bool showHidden = CVarGetInteger("gCheckTrackerOptionShowHidden", 0); bool hideIncomplete = CVarGetInteger("gCheckTrackerAreaIncompleteHide", 0); bool hideComplete = CVarGetInteger("gCheckTrackerAreaCompleteHide", 0); bool collapseLogic; bool doingCollapseOrExpand = optExpandAll || optCollapseAll; bool isThisAreaSpoiled; RandomizerCheckArea lastArea = RCAREA_INVALID; Color_RGBA8 areaCompleteColor = CVarGetColor("gCheckTrackerAreaMainCompleteColor", Color_Main_Default); Color_RGBA8 areaIncompleteColor = CVarGetColor("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default); Color_RGBA8 extraCompleteColor = CVarGetColor("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default); Color_RGBA8 extraIncompleteColor = CVarGetColor("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default); Color_RGBA8 mainColor; Color_RGBA8 extraColor; std::string stemp; s32 areaMask = 1; for (auto& [rcArea, checks] : checksByArea) { RandomizerCheckArea thisArea = currentArea; thisAreaFullyChecked = (areaChecksGotten[rcArea] == areaCheckTotals[rcArea]); //Last Area needs to be cleaned up if (lastArea != RCAREA_INVALID && doDraw) { UIWidgets::PaddedSeparator(); } lastArea = rcArea; if (previousShowHidden != showHidden) { previousShowHidden = showHidden; doAreaScroll = true; } 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(rcArea) + "##TreeNode"; ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f)); 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, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f)); isThisAreaSpoiled = areasSpoiled & areaMask || CVarGetInteger("gCheckTrackerOptionMQSpoilers", 0) || !IS_RANDO || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_NONE || OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SELECTION || (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_RANDOM) == RO_MQ_DUNGEONS_SET_NUMBER && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_MQ_DUNGEON_COUNT) == 12); if (isThisAreaSpoiled) { if (showVOrMQ && RandomizerCheckObjects::AreaIsDungeon(rcArea)) { if (OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(DungeonSceneLookupByArea(rcArea))->IsMQ()) ImGui::Text("(%d/%d) - MQ", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); else ImGui::Text("(%d/%d) - Vanilla", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); } else { ImGui::Text("(%d/%d)", areaChecksGotten[rcArea], areaCheckTotals[rcArea]); } } else { ImGui::Text("???"); } ImGui::PopStyleColor(); //Keep areas loaded between transitions if (thisArea == rcArea && doAreaScroll) { ImGui::SetScrollHereY(0.0f); doAreaScroll = false; } for (auto rc : checks) { if (doDraw && isThisAreaSpoiled && IsVisibleInCheckTracker(rc)) { DrawLocation(rc); } } if (doDraw) { ImGui::TreePop(); } } areaMask <<= 1; } 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, bool& open, ImGuiWindowFlags flags) { ImGuiWindowFlags windowFlags = flags; if (windowFlags == 0) { windowFlags |= ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoFocusOnAppearing; } if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); windowFlags |= ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar; if (!CVarGetInteger("gCheckTrackerHudEditMode", 0)) { windowFlags |= ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove; } } ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(Color_Background.r / 255.0f, Color_Background.g / 255.0f, Color_Background.b / 255.0f, Color_Background.a / 255.0f)); ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::Begin(UniqueName.c_str(), &open, windowFlags); } void EndFloatWindows() { ImGui::PopStyleVar(); ImGui::PopStyleColor(); ImGui::PopStyleColor(); ImGui::End(); } void LoadSettings() { //If in randomzer (n64ddFlag), then get the setting and check if in general we should be showing the settings //If in vanilla, _try_ to show items that at least are needed for 100% showShops = IS_RANDO ? ( OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_OFF && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHOPSANITY) != RO_SHOPSANITY_ZERO_ITEMS) : false; showBeans = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MAGIC_BEANS) == RO_GENERIC_YES : true; showScrubs = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SCRUBS) != RO_SCRUBS_OFF : false; showMerchants = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_OFF : true; showBeehives = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEEHIVES) == RO_GENERIC_YES : false; showCows = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_COWS) == RO_GENERIC_YES : false; showAdultTrade = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ADULT_TRADE) == RO_GENERIC_YES : true; showKokiriSword = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_KOKIRI_SWORD) == RO_GENERIC_YES : true; showMasterSword = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MASTER_SWORD) == RO_GENERIC_YES : true; showWeirdEgg = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WEIRD_EGG) == RO_GENERIC_YES : true; showGerudoCard = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD) == RO_GENERIC_YES : true; showFrogSongRupees = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_FROG_SONG_RUPEES) == RO_GENERIC_YES : false; showStartingMapsCompasses = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_MAPANDCOMPASS) != RO_DUNGEON_ITEM_LOC_VANILLA : false; showKeysanity = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA : false; showBossKeysanity = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_BOSS_KEYSANITY) != RO_DUNGEON_ITEM_LOC_VANILLA : false; showGerudoFortressKeys = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_KEYS) != RO_GERUDO_KEYS_VANILLA : false; showGanonBossKey = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GANONS_BOSS_KEY) != RO_GANON_BOSS_KEY_VANILLA : false; showOcarinas = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_OCARINA) == RO_GENERIC_YES : false; show100SkullReward = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_100_GS_REWARD) == RO_GENERIC_YES : false; showLinksPocket = IS_RANDO ? // don't show Link's Pocket if not randomizer, or if rando and pocket is disabled OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LINKS_POCKET) != RO_LINKS_POCKET_NOTHING :false; hideTriforceCompleted = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT) != RO_GENERIC_ON : false; if (IS_RANDO) { switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TOKENS)) { case RO_TOKENSANITY_ALL: showOverworldTokens = true; showDungeonTokens = true; break; case RO_TOKENSANITY_OVERWORLD: showOverworldTokens = true; showDungeonTokens = false; break; case RO_TOKENSANITY_DUNGEONS: showOverworldTokens = false; showDungeonTokens = true; break; default: showOverworldTokens = false; showDungeonTokens = false; break; } } else { // Vanilla showOverworldTokens = true; showDungeonTokens = true; } fortressFast = false; fortressNormal = false; switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_GERUDO_FORTRESS)) { case RO_GF_OPEN: showGerudoFortressKeys = false; showGerudoCard = false; break; case RO_GF_FAST: fortressFast = true; break; case RO_GF_NORMAL: fortressNormal = true; break; } fishsanityMode = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY); fishsanityPondCount = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY_POND_COUNT); fishsanityAgeSplit = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_FISHSANITY_AGE_SPLIT); } bool IsVisibleInCheckTracker(RandomizerCheck rc) { Rando::Location* loc = Rando::StaticData::GetLocation(rc); if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LOGIC_RULES) != RO_LOGIC_VANILLA) { return (loc->GetArea() != RCAREA_INVALID) && // don't show Invalid locations (loc->GetRCType() != RCTYPE_GOSSIP_STONE) && //TODO: Don't show hints until tracker supports them (loc->GetRCType() != RCTYPE_CHEST_GAME) && // don't show non final reward chest game checks until we support shuffling them (rc != RC_HC_ZELDAS_LETTER) && // don't show zeldas letter until we support shuffling it (rc != RC_LINKS_POCKET || showLinksPocket) && (!RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()) || loc->GetQuest() == RCQUEST_BOTH || loc->GetQuest() == RCQUEST_MQ && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsMQ() || loc->GetQuest() == RCQUEST_VANILLA && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsVanilla() ) && (loc->GetRCType() != RCTYPE_SHOP || (showShops && (!hideShopRightChecks || hideShopRightChecks && loc->GetActorParams() > 0x03))) && (loc->GetRandomizerCheck() != RC_MARKET_BOMBCHU_BOWLING_BOMBCHUS) && (rc != RC_TRIFORCE_COMPLETED || !hideTriforceCompleted) && (rc != RC_GIFT_FROM_SAGES || !IS_RANDO) && (loc->GetRCType() != RCTYPE_SCRUB || showScrubs || rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || // The 3 scrubs that are always randomized rc == RC_HF_DEKU_SCRUB_GROTTO || rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT ) && (loc->GetRCType() != RCTYPE_MERCHANT || showMerchants) && (loc->GetRCType() != RCTYPE_BEEHIVE || showBeehives) && (loc->GetRCType() != RCTYPE_OCARINA || showOcarinas) && (loc->GetRCType() != RCTYPE_SKULL_TOKEN || alwaysShowGS || (showOverworldTokens && RandomizerCheckObjects::AreaIsOverworld(loc->GetArea())) || (showDungeonTokens && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea())) ) && (loc->GetRCType() != RCTYPE_COW || showCows) && (loc->GetRCType() != RCTYPE_FISH || OTRGlobals::Instance->gRandoContext->GetFishsanity()->GetFishLocationIncluded(loc)) && (loc->GetRCType() != RCTYPE_ADULT_TRADE || showAdultTrade || rc == RC_KAK_ANJU_AS_ADULT || // adult trade checks that are always shuffled rc == RC_DMT_TRADE_CLAIM_CHECK // even when shuffle adult trade is off ) && (rc != RC_KF_KOKIRI_SWORD_CHEST || showKokiriSword) && (rc != RC_TOT_MASTER_SWORD || showMasterSword) && (rc != RC_ZR_MAGIC_BEAN_SALESMAN || showBeans) && (rc != RC_HC_MALON_EGG || showWeirdEgg) && (loc->GetRCType() != RCTYPE_FROG_SONG || showFrogSongRupees) && (loc->GetRCType() != RCTYPE_MAP_COMPASS || showStartingMapsCompasses) && (loc->GetRCType() != RCTYPE_SMALL_KEY || showKeysanity) && (loc->GetRCType() != RCTYPE_BOSS_KEY || showBossKeysanity) && (loc->GetRCType() != RCTYPE_GANON_BOSS_KEY || showGanonBossKey) && (rc != RC_KAK_100_GOLD_SKULLTULA_REWARD || show100SkullReward) && (loc->GetRCType() != RCTYPE_GF_KEY && rc != RC_GF_GERUDO_MEMBERSHIP_CARD || (showGerudoCard && rc == RC_GF_GERUDO_MEMBERSHIP_CARD) || (fortressNormal && showGerudoFortressKeys && loc->GetRCType() == RCTYPE_GF_KEY) || (fortressFast && showGerudoFortressKeys && rc == RC_GF_NORTH_F1_CARPENTER) ); } else if (loc->IsVanillaCompletion()) { return (loc->GetQuest() == RCQUEST_BOTH || (loc->GetQuest() == RCQUEST_MQ && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsMQ()) || (loc->GetQuest() == RCQUEST_VANILLA && OTRGlobals::Instance->gRandoContext->GetDungeons()->GetDungeonFromScene(loc->GetScene())->IsVanilla()) || rc == RC_GIFT_FROM_SAGES) && rc != RC_LINKS_POCKET; } return false; } void UpdateInventoryChecks() { //For all the areas with compasses, if you have one, spoil the area for (auto [scene, area] : DungeonRCAreasBySceneID) { if (CHECK_DUNGEON_ITEM(DUNGEON_MAP, scene)) { areasSpoiled |= (1 << area); } } } void UpdateAreaFullyChecked(RandomizerCheckArea area) { } void UpdateAreas(RandomizerCheckArea area) { areasFullyChecked[area] = areaChecksGotten[area] == checksByArea.find(area)->second.size(); if (areaChecksGotten[area] != 0 || RandomizerCheckObjects::AreaIsOverworld(area)) { areasSpoiled |= (1 << area); } } void UpdateAllOrdering() { // Sort the entire thing for (int i = 0; i < RCAREA_INVALID; i++) { UpdateOrdering(static_cast(i)); } } void UpdateOrdering(RandomizerCheckArea rcArea) { // Sort a single area if(checksByArea.contains(rcArea)) { std::sort(checksByArea.find(rcArea)->second.begin(), checksByArea.find(rcArea)->second.end(), CompareChecks); } } bool IsEoDCheck(RandomizerCheckType type) { return type == RCTYPE_BOSS_HEART_OR_OTHER_REWARD || type == RCTYPE_DUNGEON_REWARD; } bool CompareChecks(RandomizerCheck i, RandomizerCheck j) { RandomizerCheckTrackerData iShow = gSaveContext.checkTrackerData[i]; RandomizerCheckTrackerData jShow = gSaveContext.checkTrackerData[j]; Rando::Location* x = Rando::StaticData::GetLocation(i); Rando::Location* y = Rando::StaticData::GetLocation(j); bool iCollected = iShow.status == RCSHOW_COLLECTED || iShow.status == RCSHOW_SAVED; bool iSaved = iShow.status == RCSHOW_SAVED; bool jCollected = jShow.status == RCSHOW_COLLECTED || jShow.status == RCSHOW_SAVED; bool jSaved = jShow.status == RCSHOW_SAVED; if (!iCollected && jCollected) { return true; } else if (iCollected && !jCollected) { return false; } if (!iSaved && jSaved) { return true; } else if (iSaved && !jSaved) { return false; } if (!iShow.skipped && jShow.skipped) { return true; } else if (iShow.skipped && !jShow.skipped) { return false; } if (!IsEoDCheck(x->GetRCType()) && IsEoDCheck(y->GetRCType())) { return true; } else if (IsEoDCheck(x->GetRCType()) && !IsEoDCheck(y->GetRCType())) { return false; } if (i < j) { return true; } else if (i > j) { return false; } return false; } bool IsHeartPiece(GetItemID giid) { return giid == GI_HEART_PIECE || giid == GI_HEART_PIECE_WIN; } void DrawLocation(RandomizerCheck rc) { Color_RGBA8 mainColor; Color_RGBA8 extraColor; std::string txt; bool showHidden = CVarGetInteger("gCheckTrackerOptionShowHidden", 0); Rando::Location* loc = Rando::StaticData::GetLocation(rc); Rando::ItemLocation* itemLoc = OTRGlobals::Instance->gRandoContext->GetItemLocation(rc); RandomizerCheckTrackerData checkData = gSaveContext.checkTrackerData[rc]; RandomizerCheckStatus status = checkData.status; bool skipped = checkData.skipped; if (status == RCSHOW_COLLECTED) { if (!showHidden && CVarGetInteger("gCheckTrackerCollectedHide", 0)) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO ? CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default) : CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default); } else if (status == RCSHOW_SAVED) { if (!showHidden && CVarGetInteger("gCheckTrackerSavedHide", 0)) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO ? CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default) : CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default); } else if (skipped) { if (!showHidden && CVarGetInteger("gCheckTrackerSkippedHide", 0)) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO ? CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default) : CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default); } else if (status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED) { if (!showHidden && CVarGetInteger("gCheckTrackerSeenHide", 0)) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO ? CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default) : CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default); } else if (status == RCSHOW_SCUMMED) { if (!showHidden && CVarGetInteger("gCheckTrackerScummedHide", 0)) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO ? CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default) : CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default); } else if (status == RCSHOW_UNCHECKED) { if (!showHidden && CVarGetInteger("gCheckTrackerUncheckedHide", 0)) { return; } mainColor = !IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID()) && !IS_RANDO ? CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default) : CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default); extraColor = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default); } //Main Text if (checkNameOverrides.contains(loc->GetRandomizerCheck())) { txt = checkNameOverrides[loc->GetRandomizerCheck()]; } else { txt = loc->GetShortName(); } if (lastLocationChecked == loc->GetRandomizerCheck()) { txt = "* " + txt; } // Draw button - for Skipped/Seen/Scummed/Unchecked only if (status == RCSHOW_UNCHECKED || status == RCSHOW_SEEN || status == RCSHOW_IDENTIFIED || status == RCSHOW_SCUMMED || skipped) { if (UIWidgets::StateButton(std::to_string(rc).c_str(), skipped ? ICON_FA_PLUS : ICON_FA_TIMES)) { if (skipped) { gSaveContext.checkTrackerData[rc].skipped = false; areaChecksGotten[loc->GetArea()]--; } else { gSaveContext.checkTrackerData[rc].skipped = true; areaChecksGotten[loc->GetArea()]++; } UpdateOrdering(loc->GetArea()); UpdateInventoryChecks(); SaveManager::Instance->SaveSection(gSaveContext.fileNum, sectionId, true); } } else { ImGui::InvisibleButton("", ImVec2(20.0f, 10.0f)); } ImGui::SameLine(); //Draw ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(mainColor.r / 255.0f, mainColor.g / 255.0f, mainColor.b / 255.0f, mainColor.a / 255.0f)); ImGui::Text("%s", txt.c_str()); ImGui::PopStyleColor(); //Draw the extra info txt = ""; if (checkData.hintItem != 0) { // TODO hints } else if (status != RCSHOW_UNCHECKED) { switch (status) { case RCSHOW_SAVED: case RCSHOW_COLLECTED: case RCSHOW_SCUMMED: if (IS_RANDO) { txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language); } else { if (IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID())) { if (gSaveContext.language == LANGUAGE_ENG || gSaveContext.language == LANGUAGE_GER) { txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().english; } else if (gSaveContext.language == LANGUAGE_FRA) { txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().french; } } } break; case RCSHOW_IDENTIFIED: case RCSHOW_SEEN: if (IS_RANDO) { if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP) { if (status == RCSHOW_IDENTIFIED) { txt = OTRGlobals::Instance->gRandoContext->overrides[rc].GetTrickName().GetForLanguage(gSaveContext.language); } else { txt = Rando::StaticData::RetrieveItem(OTRGlobals::Instance->gRandoContext->overrides[rc].LooksLike()).GetName().GetForLanguage(gSaveContext.language); } } else { txt = itemLoc->GetPlacedItem().GetName().GetForLanguage(gSaveContext.language); } if (status == RCSHOW_IDENTIFIED) { txt += fmt::format(" - {}", gSaveContext.checkTrackerData[rc].price); } } else { if (IsHeartPiece((GetItemID)Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetItemID())) { if (gSaveContext.language == LANGUAGE_ENG || gSaveContext.language == LANGUAGE_GER) { txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().english; } else if (gSaveContext.language == LANGUAGE_FRA) { txt = Rando::StaticData::RetrieveItem(loc->GetVanillaItem()).GetName().french; } } } break; } } if (txt == "" && skipped) { txt = "Skipped"; // TODO language } if (txt != "") { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(extraColor.r / 255.0f, extraColor.g / 255.0f, extraColor.b / 255.0f, extraColor.a / 255.0f)); ImGui::SameLine(); ImGui::Text(" (%s)", txt.c_str()); ImGui::PopStyleColor(); } } static std::set rainbowCVars = { "gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor", "gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor", "gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor", "gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor", "gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", "gCheckTrackerCollectedMainColor", "gCheckTrackerCollectedExtraColor", "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", }; int hue = 0; void RainbowTick() { float freqHue = hue * 2 * M_PI / (360 * CVarGetFloat("gCosmetics.RainbowSpeed", 0.6f)); for (auto& cvar : rainbowCVars) { if (CVarGetInteger((cvar + "RBM").c_str(), 0) == 0) { continue; } Color_RGBA8 newColor; newColor.r = sin(freqHue + 0) * 127 + 128; newColor.g = sin(freqHue + (2 * M_PI / 3)) * 127 + 128; newColor.b = sin(freqHue + (4 * M_PI / 3)) * 127 + 128; newColor.a = 255; CVarSetColor(cvar.c_str(), newColor); } hue++; hue %= 360; } 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, const char* tooltip) { Color_RGBA8 cvarMainColor = CVarGetColor(cvarMainName, main_default_color); Color_RGBA8 cvarExtraColor = CVarGetColor(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, ImVec4(main_color.r, main_color.g, main_color.b, main_color.a), ImVec4(main_default_color.r, main_default_color.g, main_default_color.b, main_default_color.a))) { main_color = CVarGetColor(cvarMainName, main_default_color); }; ImGui::PopItemWidth(); ImGui::TableNextColumn(); ImGui::AlignTextToFramePadding(); ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x); if (UIWidgets::EnhancementColor("Details", cvarExtraName, ImVec4(extra_color.r, extra_color.g, extra_color.b, extra_color.a), ImVec4(extra_default_color.r, extra_default_color.g, extra_default_color.b, extra_default_color.a))) { extra_color = CVarGetColor(cvarExtraName, extra_default_color); } ImGui::PopItemWidth(); ImGui::EndTable(); } } if (tooltip != "") { ImGui::SameLine(); ImGui::Text(" ?"); UIWidgets::Tooltip(tooltip); } } static const char* windowType[] = { "Floating", "Window" }; static const char* displayType[] = { "Always", "Combo Button Hold" }; static const char* buttonStrings[] = { "A Button", "B Button", "C-Up", "C-Down", "C-Left", "C-Right", "L Button", "Z Button", "R Button", "Start", "D-Up", "D-Down", "D-Left", "D-Right" }; void CheckTrackerSettingsWindow::DrawElement() { ImGui::SetNextWindowSize(ImVec2(600, 375), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Check Tracker Settings", &mIsVisible, 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::PushItemWidth(ImGui::GetContentRegionAvail().x); if (UIWidgets::EnhancementColor("BG Color", "gCheckTrackerBgColor", ImVec4(Color_Background.r, Color_Background.g, Color_Background.b, Color_Background.a), ImVec4(Color_Bg_Default.r, Color_Bg_Default.g, Color_Bg_Default.b, Color_Bg_Default.a), false, true)) { Color_Background = CVarGetColor("gCheckTrackerBgColor", Color_Bg_Default); } ImGui::PopItemWidth(); UIWidgets::LabeledRightAlignedEnhancementCombobox("Window Type", "gCheckTrackerWindowType", windowType, TRACKER_WINDOW_WINDOW); if (CVarGetInteger("gCheckTrackerWindowType", TRACKER_WINDOW_WINDOW) == TRACKER_WINDOW_FLOATING) { UIWidgets::EnhancementCheckbox("Enable Dragging", "gCheckTrackerHudEditMode"); UIWidgets::EnhancementCheckbox("Only enable while paused", "gCheckTrackerShowOnlyPaused"); UIWidgets::LabeledRightAlignedEnhancementCombobox("Display Mode", "gCheckTrackerDisplayType", displayType, 0); if (CVarGetInteger("gCheckTrackerDisplayType", TRACKER_DISPLAY_ALWAYS) == TRACKER_DISPLAY_COMBO_BUTTON) { UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 1", "gCheckTrackerComboButton1", buttonStrings, TRACKER_COMBO_BUTTON_L); UIWidgets::LabeledRightAlignedEnhancementCombobox("Combo Button 2", "gCheckTrackerComboButton2", buttonStrings, TRACKER_COMBO_BUTTON_R); } } UIWidgets::EnhancementCheckbox("Vanilla/MQ Dungeon Spoilers", "gCheckTrackerOptionMQSpoilers"); UIWidgets::Tooltip("If enabled, Vanilla/MQ dungeons will show on the tracker immediately. Otherwise, Vanilla/MQ dungeon locations must be unlocked."); if (UIWidgets::EnhancementCheckbox("Hide right-side shop item checks", "gCheckTrackerOptionHideRightShopChecks", false, "", UIWidgets::CheckboxGraphics::Cross, true)) { hideShopRightChecks = !hideShopRightChecks; RecalculateAreaTotals(); } UIWidgets::Tooltip("If enabled, will prevent the tracker from displaying slots 1-4 in all shops."); if (UIWidgets::EnhancementCheckbox("Always show gold skulltulas", "gCheckTrackerOptionAlwaysShowGSLocs", false, "")) { alwaysShowGS = !alwaysShowGS; RecalculateAreaTotals(); } UIWidgets::Tooltip("If enabled, will show GS locations in the tracker regardless of tokensanity settings."); ImGui::TableNextColumn(); CheckTracker::ImGuiDrawTwoColorPickerSection("Area Incomplete", "gCheckTrackerAreaMainIncompleteColor", "gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Main, Color_Area_Incomplete_Extra, Color_Main_Default, Color_Area_Incomplete_Extra_Default, "gCheckTrackerAreaIncompleteHide", ""); CheckTracker::ImGuiDrawTwoColorPickerSection("Area Complete", "gCheckTrackerAreaMainCompleteColor", "gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Main, Color_Area_Complete_Extra, Color_Main_Default, Color_Area_Complete_Extra_Default, "gCheckTrackerAreaCompleteHide", ""); CheckTracker::ImGuiDrawTwoColorPickerSection("Unchecked", "gCheckTrackerUncheckedMainColor", "gCheckTrackerUncheckedExtraColor", Color_Unchecked_Main, Color_Unchecked_Extra, Color_Main_Default, Color_Unchecked_Extra_Default, "gCheckTrackerUncheckedHide", "Checks you have not interacted with at all."); CheckTracker::ImGuiDrawTwoColorPickerSection("Skipped", "gCheckTrackerSkippedMainColor", "gCheckTrackerSkippedExtraColor", Color_Skipped_Main, Color_Skipped_Extra, Color_Main_Default, Color_Skipped_Extra_Default, "gCheckTrackerSkippedHide", ""); CheckTracker::ImGuiDrawTwoColorPickerSection("Seen", "gCheckTrackerSeenMainColor", "gCheckTrackerSeenExtraColor", Color_Seen_Main, Color_Seen_Extra, Color_Main_Default, Color_Seen_Extra_Default, "gCheckTrackerSeenHide", "Used for shops. Shows item names for shop slots when walking in, and prices when highlighting them in buy mode."); CheckTracker::ImGuiDrawTwoColorPickerSection("Scummed", "gCheckTrackerScummedMainColor", "gCheckTrackerScummedExtraColor", Color_Scummed_Main, Color_Scummed_Extra, Color_Main_Default, Color_Scummed_Extra_Default, "gCheckTrackerScummedHide", "Checks you collect, but then reload before saving so you no longer have them."); //CheckTracker::ImGuiDrawTwoColorPickerSection("Hinted (WIP)", "gCheckTrackerHintedMainColor", "gCheckTrackerHintedExtraColor", Color_Hinted_Main, Color_Hinted_Extra, Color_Main_Default, Color_Hinted_Extra_Default, "gCheckTrackerHintedHide", ""); CheckTracker::ImGuiDrawTwoColorPickerSection("Collected", "gCheckTrackerCollectedMainColor", "gCheckTrackerCollectedExtraColor", Color_Collected_Main, Color_Collected_Extra, Color_Main_Default, Color_Collected_Extra_Default, "gCheckTrackerCollectedHide", "Checks you have collected without saving or reloading yet."); CheckTracker::ImGuiDrawTwoColorPickerSection("Saved", "gCheckTrackerSavedMainColor", "gCheckTrackerSavedExtraColor", Color_Saved_Main, Color_Saved_Extra, Color_Main_Default, Color_Saved_Extra_Default, "gCheckTrackerSavedHide", "Checks that you saved the game while having collected."); ImGui::PopStyleVar(1); ImGui::EndTable(); ImGui::End(); } void CheckTrackerWindow::InitElement() { Color_Background = CVarGetColor("gCheckTrackerBgColor", Color_Bg_Default); Color_Area_Incomplete_Main = CVarGetColor("gCheckTrackerAreaMainIncompleteColor", Color_Main_Default); Color_Area_Incomplete_Extra = CVarGetColor("gCheckTrackerAreaExtraIncompleteColor", Color_Area_Incomplete_Extra_Default); Color_Area_Complete_Main = CVarGetColor("gCheckTrackerAreaMainCompleteColor", Color_Main_Default); Color_Area_Complete_Extra = CVarGetColor("gCheckTrackerAreaExtraCompleteColor", Color_Area_Complete_Extra_Default); Color_Unchecked_Main = CVarGetColor("gCheckTrackerUncheckedMainColor", Color_Main_Default); Color_Unchecked_Extra = CVarGetColor("gCheckTrackerUncheckedExtraColor", Color_Unchecked_Extra_Default); Color_Skipped_Main = CVarGetColor("gCheckTrackerSkippedMainColor", Color_Main_Default); Color_Skipped_Extra = CVarGetColor("gCheckTrackerSkippedExtraColor", Color_Skipped_Extra_Default); Color_Seen_Main = CVarGetColor("gCheckTrackerSeenMainColor", Color_Main_Default); Color_Seen_Extra = CVarGetColor("gCheckTrackerSeenExtraColor", Color_Seen_Extra_Default); Color_Hinted_Main = CVarGetColor("gCheckTrackerHintedMainColor", Color_Main_Default); Color_Hinted_Extra = CVarGetColor("gCheckTrackerHintedExtraColor", Color_Hinted_Extra_Default); Color_Collected_Main = CVarGetColor("gCheckTrackerCollectedMainColor", Color_Main_Default); Color_Collected_Extra = CVarGetColor("gCheckTrackerCollectedExtraColor", Color_Collected_Extra_Default); Color_Scummed_Main = CVarGetColor("gCheckTrackerScummedMainColor", Color_Main_Default); Color_Scummed_Extra = CVarGetColor("gCheckTrackerScummedExtraColor", Color_Scummed_Extra_Default); Color_Saved_Main = CVarGetColor("gCheckTrackerSavedMainColor", Color_Main_Default); Color_Saved_Extra = CVarGetColor("gCheckTrackerSavedExtraColor", Color_Saved_Extra_Default); SaveManager::Instance->AddInitFunction(InitTrackerData); sectionId = SaveManager::Instance->AddSaveFunction("trackerData", 1, SaveFile, true, -1); SaveManager::Instance->AddLoadFunction("trackerData", 1, LoadFile); GameInteractor::Instance->RegisterGameHook(CheckTrackerLoadGame); GameInteractor::Instance->RegisterGameHook([](uint32_t fileNum) { Teardown(); }); GameInteractor::Instance->RegisterGameHook(CheckTrackerItemReceive); GameInteractor::Instance->RegisterGameHook(CheckTrackerFrame); GameInteractor::Instance->RegisterGameHook(CheckTrackerTransition); GameInteractor::Instance->RegisterGameHook(CheckTrackerShopSlotChange); GameInteractor::Instance->RegisterGameHook(CheckTrackerSceneFlagSet); GameInteractor::Instance->RegisterGameHook(CheckTrackerFlagSet); hideShopRightChecks = CVarGetInteger("gCheckTrackerOptionHideRightShopChecks", 1); alwaysShowGS = CVarGetInteger("gCheckTrackerOptionAlwaysShowGSLocs", 0); //LocationTable_Init(); } } // namespace CheckTracker